I have the following types:
internal partial interface I1 { }
internal partial struct S1 : I1 { }
I use them with this code:
IEnumerable<S1> test = Enumerable.Empty<S1>();
IEnumerable<I1> test2 = test;
But test2 assignment fails with "Cannot implicitly convert type IEnumerable<S1>' to 'IEnumerable<I1>'" because variance does not seem to work with struct.
Why do we have this restriction?
Is there any plan to support this in the future?
The variance limitations are imposed by the CLR. My understanding is that this is due to the fact that structs all have different sizes and thus can't share a JITed implementation. References all have the same size so the JIT can compile the generic type once and share it.
This isn't a "limitation" of CLR, it's simply how value and reference types work. An interface is a reference type and going from a value type to a reference type requires a boxing conversion. Variance requires reference conversions since these are basically no-op, the compiler and the runtime can lie and claim that IEnumerator<RefType>.Current returns IRefType even if it actually returns RefType.
The C# compiler could implement the cast from IEnumerable<S1> to IEnumerable<I1> by doing something like from s in es1 select (I1)s. But that wouldn't be a reference conversion anymore, it would be some kind of new alien conversion that resembles boxing conversion somewhat and has the weird characteristic that the conversion result cannot be converted back to the original value (it's actually possible but the compiler needs to stand on its head to do that).
@mikedn I'm completely aware about the boxing conversion. And, of course with IEnumerable<>, it's very easy to workaround. However, in some other scenarios, it would be an interesting feature IMHO.
It would be an interesting feature IMHO.
Well, it's an interesting feature that cannot be implemented well :smile:
Most helpful comment
This isn't a "limitation" of CLR, it's simply how value and reference types work. An interface is a reference type and going from a value type to a reference type requires a boxing conversion. Variance requires reference conversions since these are basically no-op, the compiler and the runtime can lie and claim that
IEnumerator<RefType>.CurrentreturnsIRefTypeeven if it actually returnsRefType.The C# compiler could implement the cast from
IEnumerable<S1>toIEnumerable<I1>by doing something likefrom s in es1 select (I1)s. But that wouldn't be a reference conversion anymore, it would be some kind of new alien conversion that resembles boxing conversion somewhat and has the weird characteristic that the conversion result cannot be converted back to the original value (it's actually possible but the compiler needs to stand on its head to do that).