Reputation: 686
In the sample code below, why does the call to ArrayMethod fail for a genric type when I don't include the class
constraint
public interface IFoo { }
public interface IBar : IFoo { }
public class Test
{
public void ClassConstraintTest<T>() where T : class, IFoo
{
T[] variable = new T[0];
ArrayMethod(variable);
}
public void GenericTest<T>() where T : IFoo
{
T[] variable = new T[0];
ArrayMethod(variable); // Compilation error: Can't convert T[] to IFoo[]
}
public void InheritanceTest()
{
IBar[] variable = new IBar[0];
ArrayMethod(variable);
}
public void ArrayMethod(IFoo[] args) { }
}
Upvotes: 9
Views: 284
Reputation: 5413
In short: array covariance only works when both arrays are of a reference (class
) type.
To understand this, you have to understand the memory layout of different types of arrays. In C# we have value arrays (int[]
, float[]
, DateTime[]
, any user-defined struct[]
) where each thing is stored sequentially inside the array, and reference arrays (object[]
, string[]
, any user-defined class[]
, interface[]
, or delegate[]
) where references are stored sequentially inside the array and the objects are stored outside the array wherever they fit in memory.
When you request that the method work on any T
(without the : class
constraint) you allow for either of those two types of arrays, but the compiler knows for a fact that any int[]
(or any other value array) is not going to somehow magically become a valid IFoo[]
(or any other reference array) and forbids the conversion. This happens even if your struct implements IFoo
for no other reason than IFoo[]
is a reference array and a T[]
will be a value array.
However, when you specify that T
is a reference type (i.e. the class
declaration), it is now possible that the T[]
is a valid IFoo[]
because they are both reference arrays. So the compiler allows the code using the array covariance rules (which state you can use T[]
where IFoo[]
is required because T
is a subtype of IFoo
).
Upvotes: 5
Reputation: 172230
It's because array covariance, i.e., the fact that MySubtype[]
is a subtype of MyType[]
, only works for reference types. The class
constraint ensures that T
is a reference type.
(Note that, in retrospect, array covariance is considered to be a bad idea. Try to avoid it if you can, for example, by making ArrayMethod
generic or by using IEnumerable<IFoo>
instead.)
Upvotes: 9