C# 传递数组

C# 传递数组,c#,generics,C#,Generics,在下面的示例代码中,当我没有包含类约束时,为什么对genric类型的ArrayMethod调用失败 public interface IFoo { } public interface IBar : IFoo { } public class Test { public void ClassConstraintTest<T>() where T : class, IFoo { T[] variable = new T[0]; Arra

在下面的示例代码中,当我没有包含
约束时,为什么对genric类型的ArrayMethod调用失败

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) { }
}
公共接口IFoo{}
公共接口IBar:IFoo{}
公开课考试
{
public void ClassConstraintTest(),其中T:class,IFoo
{
T[]变量=新T[0];
数组方法(变量);
}
public void GenericTest(),其中T:IFoo
{
T[]变量=新的T[0];
ArrayMethod(变量);//编译错误:无法将t[]转换为IFoo[]
}
公共无效继承测试()
{
IBar[]变量=新的IBar[0];
数组方法(变量);
}
公共空数组方法(IFoo[]args){}
}
这是因为,也就是说,
MySubtype[]
MyType[]
的一个子类型,只适用于引用类型。
class
约束确保
T
是引用类型


(请注意,回想起来,数组协方差是。如果可以,请尝试避免它,例如,将
ArrayMethod
设置为泛型,或者改用
IEnumerable

简而言之:数组协方差仅在两个数组都是引用(
class
)类型时起作用

要理解这一点,您必须了解不同类型阵列的内存布局。在C#中,我们有值数组(
int[]
float[]
DateTime[]
,任何用户定义的
struct[]
),其中每个东西都顺序存储在数组中,还有引用数组(
对象[]
字符串[]
,任何用户定义的
类[]
接口[]
,或
委托[]
),其中引用按顺序存储在数组内部,对象存储在数组外部的内存中

当您请求该方法在任何
T
(没有
:class
约束)上工作时,您允许这两种类型的数组中的任何一种,但编译器知道一个事实,即任何
int[]
(或任何其他值数组)都不会以某种方式神奇地成为有效的
IFoo[]
(或任何其他引用数组)并禁止转换。即使您的结构实现了
IFoo
,除了
IFoo[]
是一个引用数组,而
T[]
将是一个值数组之外,也会发生这种情况


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

一个好的类型系统应该有一个协方差类型,它可以用作数组,数组中的项可以读取、交换或复制。NET和Java中的数组类型可以作为此类类型以协变方式安全地使用,也可以作为可自由写入的集合以非协变方式安全地使用。也许对数组进行多种引用会更好,它们具有不同的广告功能,但是那些简单地说协变数组是一个“错误”的人并没有真正计算出替代方案的成本。