Considering that ref structs cannot be boxed or passed as a generic type argument, implementing interfaces is pointless.
What about IDisposable
? A using
statement requires the type to be IDisposable
even if it dispatches the Dispose
call to the method on a particular type.
Ref struct is not convertible to an interface, even if it claims to be implementing one. In particilar that would not satisfy the requirememts of using
.
Well, we could change the requirements of using
, as in #11420.
It is definitely not pointless, it can used with generics without boxing. Why can it not be used as a generic type argument?
I have a specific need that is blocked by this, namely for my work with Span.Sort?
I guess it is because there is currently no way to constrain a generic type parameter to avoid possibly boxing it. Why not consider a constraint for this then? Not sure what would be good but:
internal interface ISwapper
{
void Swap(int i, int j);
}
internal ref struct Swapper<T> : ISwapper // ref structs can't inherit interfaces :( Nor be generic type parameter
{
Span<T> _items;
public Swapper(Span<T> items)
{
_items = items;
}
public void Swap(int i, int j)
{
ref T start = ref _items.DangerousGetPinnableReference();
SpanSortHelper.Swap(ref Unsafe.Add(ref start, i),
ref Unsafe.Add(ref start, j));
}
}
public void SomeMethod<TSwapper>(TSwapper swapper)
where TSwapper : ref struct
// Although it should still work for normal structs, just restrict the availability of boxing etc.
// "unboxable" :|
{
// Swapper can then be used to inject zero, one or more extra items...
}
I second @nietras. This limitation prevents the creation of a lot of very useful building blocks.
As a motivation consider this building block:
public interface IAction<T>
{
void Do(ref T value);
}
public static unsafe void ForEach<A, T>(this Memory<T> memory, A action)
where T : unmanaged
where A : struct, IAction<T>
{
fixed (T *ptr = &memory.Span.GetPinnableReference())
{
var p = ptr;
var e = ptr + memory.Span.Length;
for (; p < e; ++p)
{
action.Do(ref *ptr);
}
}
}
And then using this with some implementation:
public struct PointlesSumAction : IAction<Char>
{
public Char sum;
public void Do(ref Char value)
{
sum += value;
}
}
...
{
var a = new PointlesSumAction();
myMemory.ForEach(ref a);
}
With optimizations turned on, this compiles to a loop with Do
inlined, and the range checks being done only once for the ForEach
call.
This can be done with Memory
now, but it really belongs to Span
.
Most helpful comment
I guess it is because there is currently no way to constrain a generic type parameter to avoid possibly boxing it. Why not consider a constraint for this then? Not sure what would be good but: