Josh
Josh

Reputation: 686

Passing an array

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

Answers (2)

Kyle Sletten
Kyle Sletten

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

Heinzi
Heinzi

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

Related Questions