无法转换值类型数组params对象[] [英] Can't convert value type array to params object[]

查看:203
本文介绍了无法转换值类型数组params对象[]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果C#可以投一个int为对象,为什么不是一个对象INT [] []?

简单的程序,例如:

 无效的主要()
{
    变种一个=新的String [] {0,1};
    变种B =新INT [] {0,1};

    AssertMoreThan1(一); // 没有例外
    AssertMoreThan1(B); // 例外
}

静态无效AssertMoreThan1(params对象[] V){
    如果(v.Length == 1){
        抛出新的异常(太少参数);
    }
}
 

解决方案
  

如果C#可以投一个int为对象,为什么不是一个对象INT [] []?

您的问题可能还表示为什么是的的规则在C#中的数组转换?

他们是有点棘手,并在几个有趣的和不幸的方式打破。

首先,我们应该清楚地说明了我们所说的协方差。协方差是一个的映射的preserves一个的相关的属性。映射在这里是T去的T阵。在相关的是可以隐式转换。例如:

长颈鹿可以隐式转换为哺乳动物

这两种类型之间的关系。现在应用的映射关系的双方:

长颈鹿[] 可以被转换成哺乳动物[]

如果第一个语句的真理总是引起该第二份声明的事实 - 那就是,如果映射的 preserves 的关系的真理 - 则映射据说是协变。

作为一个缩写,而不是说映射从T到的T数组是协变映射在隐式转换关系,我们只说数组是协变,希望剩下的这些是从上下文中理解。

好了,现在我们已经定义了下来:数组的与引用类型元素的是协变在C#中。不幸的是,这是破碎的协方差:

 类哺乳动物{}
一流的长颈鹿:哺乳动物{}
类虎:哺乳动物{}
...
哺乳动物[]哺乳动物=新长颈鹿[1];
 

这是完全合法的,因为引用类型元素的数组是协变在C#。但后来这个崩溃在运行时:

 哺乳动物[0] =新虎();
 

由于是哺乳动物的长颈鹿真是一个数组的。

这意味着每次你的的一个数组,其元素的启封引用类型的,在运行时进行类型检查的和可能,如果类型检查崩溃失败的。

这是我的候选人为C#的最糟糕的功能,但它实际上的工作的。

您的问题是为什么阵列协方差不是当源阵列是值类型数组和对象数组是引用类型的数组工作吗?

由于这两件事情有不同的形式在运行时的。假设你有一个字节[] 有十个元素。保留的数组元素的实际的存储是10字节长。假设你是一个64位的机器上,你有一个 [对象] 有十个元素。存储是八倍大!

显然,你无法通过引用转换,转换的 的引用存储10个字节存储十八字节引用字节。额外的70字节不无章可循;有人来分配。

此外:谁做拳击的?如果你有十个对象的数组,每个对象是一个字节,这些字节中的每一个的盒装的。但是,在一个字节数组没有盒装。所以,当你做转换,谁做拳击?

在一般在C#中,协变的转换总是preserve重新presentation 的。的一个引用动物重新presentation是完全一样的参考长颈鹿重新presentation。但是,诚信和重新presentations参考对象是完全不同的。

人们期望铸造一个阵列到另一种不的分配并复制一个巨大的数组的。但是,我们不能有引用的身份的之间的10个字节数组和包含10个参考文献,八十字节数组,因此整个事情简直就是为非法。

现在,你可能会然后说,好吧,会发生什么情况的时候再presentations的是相同的值类型?事实上,这是非法的在C#:<​​/ P>

  INT []×=新的uint [10];
 

由于在C#中的规则是只涉及引用类型只协数组转换是合法的。但是,如果你强迫它在运行时完成:

  INT []×=(INT [])(对象)新的uint [10];
 

然后运行时允许它,因为一个4字节int和一个4字节UINT具有相同的重presentation。

如果您想了解这更好,那么你或许应该看过我整个系列文章就如何协变和逆变工作在C#:<​​/ P>

  • 全系列

  • <一个href="http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx">The不安全的参考元素的数组协方差具体

  • <一个href="http://blogs.msdn.com/b/ericlippert/archive/2009/09/24/why-is-covariance-of-value-typed-arrays-inconsistent.aspx">More对价值元素的数组协方差

If C# can cast an int to an object, why not an int[] to an object[]?

Simple Program Example:

void Main()
{
    var a = new String[]{"0", "1"};
    var b = new int[]{0, 1};

    AssertMoreThan1(a); // No Exception
    AssertMoreThan1(b); // Exception
}

static void AssertMoreThan1(params object[] v){
    if(v.Length == 1){
        throw new Exception("Too Few Parameters");
    }
}

解决方案

If C# can cast an int to an object, why not an int[] to an object[]?

Your question could be also stated as "what are the covariance rules for array conversions in C#?"

They are a bit tricky, and broken in several interesting and unfortunate ways.

First off, we should clearly state what we mean by "covariance". Covariance is the property that a mapping preserves a relationship. The mapping here is "T goes to array of T". The relationship is "can be implicitly converted". For example:

Giraffe can be implicitly converted to Mammal.

That's a relationship between two types. Now apply the mapping to both sides of the relationship:

Giraffe[] can be converted to Mammal[].

If the truth of the first statement always entails the truth of the second statement -- that is, if the mapping preserves the truth of the relationship -- then the mapping is said to be "covariant".

As a shorthand, instead of saying "the mapping from T to array of T is a covariant mapping over the implicit conversion relation", we just say "arrays are covariant" and hope that the rest of that is understood from context.

OK, now that we have the definition down: Arrays with reference type elements are covariant in C#. Tragically, this is broken covariance:

class Mammal {}
class Giraffe : Mammal {}
class Tiger : Mammal {}
...
Mammal[] mammals = new Giraffe[1];  

This is perfectly legal because arrays of reference type elements are covariant in C#. But then this crashes at runtime:

mammals[0] = new Tiger();

because mammals is really an array of Giraffes.

This means that every time you write to an array whose elements are unsealed reference types, the runtime performs a type check and might crash if the type check fails.

This is my candidate for "worst feature of C#", but it does in fact work.

Your question is "why does array covariance not work when the source array is an array of value type and the target array is an array of reference type?"

Because those two things have a different form at runtime. Suppose you have a byte[] with ten elements. The actual storage reserved for the array elements is ten bytes long. Suppose you are on a 64 bit machine and you have an object[] with ten elements. The storage is eight times bigger!

Clearly you cannot convert via reference conversion a reference to storage for ten bytes to storage for ten eight-byte references to bytes. The extra seventy bytes don't come out of nowhere; someone has to allocate them.

Moreover: who does the boxing? If you have an array of ten objects and each object is a byte, each one of those bytes is boxed. But bytes in a byte array are not boxed. So when you do the conversion, who does the boxing?

In general in C#, covariant conversions always preserve representation. The representation of a "reference to Animal" is exactly the same as the representation of "reference to Giraffe". But the representations of "int" and "reference to object" are completely different.

One expects that casting one array type to another does not allocate and copy a huge array. But we cannot have referential identity between an array of ten bytes and an array of eighty bytes containing ten references, and therefore the entire thing is simply made illegal.

Now, you might then say, well, what happens when the representations are the same for value types? In fact, this is illegal in C#:

int[] x = new uint[10];

because in C# the rule is that only covariant array conversions involving only reference types are legal. But if you force it to be done by the runtime:

int[] x = (int[])(object) new uint[10];

Then the runtime allows it because a four byte int and a four byte uint have the same representation.

If you want to understand this better then you should probably read my entire series of articles on how covariance and contravariance works in C#:

这篇关于无法转换值类型数组params对象[]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆