C# 为什么我们会得到与ref参数不匹配的ArrayTypeMismatch?
给定场景:C# 为什么我们会得到与ref参数不匹配的ArrayTypeMismatch?,c#,C#,给定场景: public class Program { static void Main() { object[] covarientArray= new A[] { new A() }; object polymorphism = new A(); object test = covarientArray[0]; M(ref polymorphism);//fine up to here M(r
public class Program
{
static void Main()
{
object[] covarientArray= new A[] { new A() };
object polymorphism = new A();
object test = covarientArray[0];
M(ref polymorphism);//fine up to here
M(ref covarientArray[0]);//ArrayTypeMismatchException
}
static void M(ref object o) { Console.WriteLine(o); }
}
class A {}
和
尝试存储时引发的异常
数组中类型错误的元素
当尝试在数组中存储错误类型的元素时,会引发此异常。例如:
A[] invalid = new A[1];
invalid[0] = "";//I can't store a type of string within an array of type of A
这种异常是如何发生的?为什么我们在调用带有
ref
参数的方法时要执行存储操作?在为错误类型的数组元素创建ref
时也会引发异常。不可否认,仅阅读例外文本并不清楚,但您链接到的页面上间接提到了这一点:
以下Microsoft中间语言(MSIL)指令引发ArrayTypeMismatchException:
阿尔德勒马
(加载元素地址)是用于创建对数组元素的引用的指令
至于为什么,它防止每个ref
参数的每个赋值都需要执行类型的运行时检查
为什么我们在调用方法时要执行存储操作
和一个参考参数
提供数组元素作为ref
参数的参数实际上是一种存储操作,至少可能是这样。(是的,您可能不会重新分配它,但编译器/运行时不一定知道这一点)
想象一下,如果您的M
方法实现是这样的:
static void M(ref object o)
{
o = new B();
}
如果我们将协变数组键入object[]
以便它编译并工作:
object[] covariantArray= new object[] { new A() };
M(ref covariantArray[0]);
Console.WriteLine(covariantArray[0].GetType().Name); //B
它运行并用一个新的B实例替换第一个元素。当然,这对于对象[]
数组是完全有效的。现在,如果我们简单地将其更改为A[]
:
object[] covariantArray= new A[] { new A() };
M(ref covariantArray[0]); //ArrayTypeMismatchException
Console.WriteLine(covariantArray[0].GetType().Name);
在处理潜在危险/灾难/狗是猫/向上是向下调用M
之前,它抛出异常
当然,删除对局部变量而不是数组元素的ref
调用或ref
,这样就可以很好地工作,因为您没有弄乱数组内容
@hvd的答案可能更正确,因为它解释了抛出的底层运行时机制,但至少这里有一个实际的演示说明为什么会这样。我对数组是协变的感到震惊。想象一下如果
M
做了这样的事情:o=new B()
;突然间,您的covarientArray
实际上是a[]
,现在它的第一个元素中包含了B
的一个实例!编辑:为了回答您的问题,在传递数组元素时执行ref
可以完全替换该元素:这是一个存储操作,至少可能是这样。@KendallFrey You和我。这与C#的伟大之处背道而驰。Eric Lippert有一篇关于它的帖子以及它被收录的原因:也许如果C#4.0中的协方差加法在C#1.0时代就已经存在,那就不同了。是的,这就是我回答的最后一句话的意思,o=new B()否则,code>需要在运行时检查o
是否真的允许存储B
。你的答案清楚多了。@hvd-两个答案都有重要信息。太糟糕了,我不能选择2。@hvd+1问题:这只是为了避免检查每个作业而进行的“优化”步骤吗?如中所示,如果您有o=newa()代码>方法中的十次,它只检查一次而不是十次?然后,如果没有“优化”,您可以传递一个ref
,而无需指定一个值,该值应使调用M
“ok”,对吗?但是,如果运行时允许执行该方法,用户反而会在o=new B()
行上得到异常,从本地上下文来看,抛出异常似乎是一个非常非常非常奇怪/糟糕的地方。对我来说,“优化”意味着它不应该改变行为,但如果允许的话,这显然是可能的。@ChrisSinclair我不确定我是否理解你的问题。从实现的角度来看,我不会将其归类为优化,因为所有实现都需要抛出异常。根据CLR规范作者的观点,我将其归类为优化,因为规范可能会说检查会在以后进行。