在下面的示例代码中,当我不包含class
约束时,为什么对genric类型的ArrayMethod调用失败
public interface IFoo { } public interface IBar : IFoo { } public class Test { public void ClassConstraintTest() where T : class, IFoo { T[] variable = new T[0]; ArrayMethod(variable); } public void GenericTest () 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) { } }
Heinzi.. 9
这是因为数组协方差,即MySubtype[]
作为子类型的事实MyType[]
,仅适用于引用类型.的class
约束确保T
是引用类型.
(请注意,回想起来,数组协方差被认为是一个坏主意.如果可以,例如,通过制作ArrayMethod
泛型或使用IEnumerable
替代,请尽量避免使用它.)
这是因为数组协方差,即MySubtype[]
作为子类型的事实MyType[]
,仅适用于引用类型.的class
约束确保T
是引用类型.
(请注意,回想起来,数组协方差被认为是一个坏主意.如果可以,例如,通过制作ArrayMethod
泛型或使用IEnumerable
替代,请尽量避免使用它.)
简而言之:只有当两个数组都是reference(class
)类型时,数组协方差才有效.
要理解这一点,您必须了解不同类型数组的内存布局.在C#,我们有值阵列(int[]
,float[]
,DateTime[]
,任何用户定义的struct[]
),其中每个事物被存储在数组内依次和参考阵列(object[]
,string[]
,任何用户定义的class[]
,interface[]
或delegate[]
)阵列和对象中,其中的引用是按顺序存储存储在数组中的任何位置.
当你请求该方法在任何T
(没有: class
约束)上工作时,你允许这两种类型的数组中的任何一种,但编译器知道任何int[]
(或任何其他值数组)不会以某种方式神奇地变为有效的事实IFoo[]
(或任何其他参考数组)并禁止转换.即使您的struct实现IFoo
的原因不是IFoo[]
参考数组,而且T[]
将是一个值数组,这种情况也会发生.
但是,当您指定它T
是引用类型(即class
声明)时,现在可能它T[]
是有效的,IFoo[]
因为它们都是引用数组.因此编译器允许使用数组协方差规则的代码(声明您可以T[]
在IFoo[]
需要的地方使用,因为T
它是子类型IFoo
).