18

考虑ITest使用协变类型参数的泛型接口、实现接口T的泛型类、类和子类:TestAB

interface ITest<out T> 
{    
  T prop{ get;}
}
class Test<T> : ITest<T>
{    
    public T prop{ get {
       return default(T);    
    }}
}
class A {    
}
class B: A {    
}

以下代码编译没有错误,但会引发运行时异常System.ArrayTypeMismatchException

ITest<A>[] a = new ITest<A>[1];
a[0] = new Test<B>(); //<-- throws runtime exception

但这段代码工作得很好:

ITest<A> r = new Test<B>();

这已在Mono 2.10.2( Unity3d 4.1) 上进行了测试。我认为这在某种程度上与数组中的破坏协方差有关(参见http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-数组协方差.aspx)。

我不清楚为什么分配数组插槽时发生的类型检查没有考虑协方差。

4

1 回答 1

2

我已经在 Windows 7 上使用 .Net 4 在 VS2010 中编译和测试了给定的代码,它工作正常,它没有给出运行时异常,因此您的问题似乎与 Mono 或 Unity 相关。

使用给定的代码,很难对问题所在做出假设。异常的确切类型和其他一些测试用例(即不使用接口的变体)将有助于缩小确切的问题范围,但这是 Mono|Unity 社区需要解决的问题。

至于它与那篇文章的链接,它是无关的。

文章描述的是以下情况:

class A { }
class B: A { }
class C: A { }

A[] a = new B[1];
a[0] = new C(); //<-- throws ArrayTypeMismatchException

为了简化 Eric 在他的文章中所说的话:

a 是一个变量,可以保存从 A 继承的任何类型的数组。

a 被分配了一个 B 的数组,因此 a 是一个 B 的数组。

当用户尝试将新的 C 分配给 a 的元素时,会出现类型不匹配,因为 a 实际上是 B 的数组,因此将 C 分配给 a 的元素相当于尝试将新的 C 分配给变量持有 B 像这样:

B b = new C();

将 a 分配为 C 的数组也会出现类似的问题。

但是,由于 a 被定义为能够保存 A 的数组,因此用户可以分配 A 的数组,这将允许它同时接受 B 和 C 的值。

正因为如此,您问题中的代码似乎与此问题有关,但实际上这并不是因为 a 被分配为 ITest 的数组,这意味着它应该能够存储 ITest 的类型并且运行时错误被抛出源于 Mono 或 Unity 运行时中的错误。

于 2013-11-13T11:51:08.700 回答