2015-12-11 47 views
9

在下面的示例代码,为什么调用ArrayMethod失败的genric类型时我不包括class约束传递一个数组

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) { } 
} 

回答

9

是因为array covariance,即事实MySubtype[]MyType[]的子类型,仅适用于参考类型。 class约束确保T是引用类型。

(需要注意的是,现在回想起来,阵列协方差为considered to be a bad idea尝试,如果你能避免它,例如,通过使ArrayMethod通用或使用IEnumerable<IFoo>代替。)

+0

一个好的类型系统应该有一个协变类型,它可以用作一个数组,它的项目可以在数组中被读取,交换或复制。 .NET和Java中的数组类型可以安全地以协变方式用作这种类型,并且可以以非协变方式安全地用作可自由写入的集合。也许最好是有多种引用数组,有不同的广告能力,但恕我直言,那些简单地说,协变阵列是一个“错误”的人并没有真正计算出替代品的成本。 – supercat

5

简而言之:数组协方差只能当两个阵列都是参考(class)类型。

要理解这一点,您必须了解不同类型数组的内存布局。在C#,我们有值阵列(int[]float[]DateTime[],任何用户定义struct[]),其中每个事物被存储在数组内依次和参考阵列(object[]string[],任何用户定义class[]interface[],或delegate[]),其中引用按顺序存储在数组中,并且对象存储在数组的外部,而不管它们在内存中的位置。

当您请求上的任何T方法工作(不: class约束)你允许任一这两个类型的阵列,但是编译器知道一个事实,即任何int[](或任何其他值阵列)不会以某种方式神奇地成为一个有效的IFoo[](或任何其他参考数组),并禁止转换。即使您的结构没有其他原因实现了IFoo,但IFoo[]是一个参考阵列,并且T[]将会是一个值数组。

但是,当您指定T是引用类型(即class声明),现在可能是T[]是一个有效的IFoo[],因为它们都是参考阵列。因此,编译器允许使用阵列协方差规则的代码(其中陈述您可以使用T[],其中IFoo[]是必需的,因为TIFoo的子类型)。