Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/308.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Can';t将值类型数组转换为参数对象[]_C#_.net - Fatal编程技术网

C# Can';t将值类型数组转换为参数对象[]

C# Can';t将值类型数组转换为参数对象[],c#,.net,C#,.net,如果C#可以将int转换为对象,为什么不将int[]转换为对象[] 简单程序示例: 事实上,你不能改变这一点。参考型数组是协变的;值类型数组不是。所以您必须使用以下其中一种: 装箱值数组: var b = new object[] {0,1}; 或者您可以使用IList: static void AssertMoreThan1(IList v) { ... (check with .Count) } 或仿制药: static void AssertMoreThan1<T>(

如果C#可以将int转换为对象,为什么不将int[]转换为对象[]

简单程序示例:
事实上,你不能改变这一点。参考型数组是协变的;值类型数组不是。所以您必须使用以下其中一种:

装箱值数组:

var b = new object[] {0,1};
或者您可以使用IList:

static void AssertMoreThan1(IList v) {
   ... (check with .Count)
}
或仿制药:

static void AssertMoreThan1<T>(T[] v) {
   ...
}
静态无效资产超过1(T[]v){
...
}
最后一个是我喜欢的

如果C#可以将int转换为对象,为什么不将int[]转换为对象[]

您的问题也可以表述为“C#中数组转换的协方差规则是什么?”

它们有点狡猾,有几种有趣而不幸的方式

首先,我们应该清楚地说明“协方差”的含义。协方差是映射保持关系的属性。这里的映射是“T到T的数组”。这种关系是“可以隐式转换的”。例如:

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

这是两种类型之间的关系。现在将映射应用于关系的两侧:

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

如果第一个语句的真值总是包含第二个语句的真值——也就是说,如果映射保持了关系的真值——那么映射称为“协变”

简而言之,我们不是说“从T到T的数组的映射是隐式转换关系上的协变映射”,而是说“数组是协变的”,希望其余的都能从上下文中理解

好了,现在我们有了定义:带有引用类型元素的数组在C#中是协变的。可悲的是,这是破碎的协方差:

class Mammal {}
class Giraffe : Mammal {}
class Tiger : Mammal {}
...
Mammal[] mammals = new Giraffe[1];  
这是完全合法的,因为引用类型元素的数组在C#中是协变的。但这会在运行时崩溃:

mammals[0] = new Tiger();
因为哺乳动物实际上是一系列长颈鹿

这意味着,每当您写入元素为未密封引用类型的数组时,运行时都会执行类型检查,如果类型检查失败,则可能会崩溃

这是我的“C#最差特性”候选项,但它确实有效

您的问题是“当源数组是值类型的数组而目标数组是引用类型的数组时,为什么数组协方差不起作用?”

因为这两件事在运行时有不同的形式。假设您有一个包含十个元素的
字节[]
。为数组元素保留的实际存储长度为10字节。假设您在一台64位机器上,有一个包含10个元素的
对象[]
。存储空间是原来的八倍

显然,您不能通过引用转换将十个字节的对存储器的引用转换为十个八字节的对存储器的引用。额外的70个字节不是凭空产生的;必须有人来分配它们

此外:谁打拳击?如果您有一个由十个对象组成的数组,并且每个对象都是一个字节,那么这些字节中的每一个都会被装箱。但字节数组中的字节不装箱。那么当你做转化时,谁来做拳击

通常在C#中,协变转换始终保持表示。“提及动物”的表述与“提及长颈鹿”的表述完全相同。但“int”和“对象引用”的表示方式完全不同

人们期望将一种数组类型转换为另一种类型不会分配和复制一个巨大的数组。但是我们不能在一个10字节的数组和一个包含10个引用的80字节的数组之间有引用标识,因此整个过程就是非法的

现在,您可能会说,当值类型的表示形式相同时会发生什么?事实上,这在C#中是非法的:

因为在C#中,规则是只涉及引用类型的协变数组转换是合法的。但如果您强制运行时执行此操作:

int[] x = (int[])(object) new uint[10];
然后运行时允许它,因为四字节int和四字节uint具有相同的表示形式

如果您想更好地理解这一点,那么您可能应该阅读我关于协方差和逆变在C#中如何工作的整个系列文章:


+1值类型数组不能隐式转换。您可以使用Linq并执行以下操作:
b.Cast().ToArray()
在本例中,我确实希望使用params定义函数,因为我主要使用标准的逗号分隔参数方法来调用它。(为了这篇文章的目的,我设置了AssertMore1函数,就像我遇到的问题的最简单的例子一样)我确实最终使用了你的第一个建议来解决我当前的问题,但我更感兴趣的是为什么(当然,Lippert先生已经非常透彻地回答了这个问题)当我不得不滚动不止一次,我已经知道谁的脸会在下面:)@MarcGravell哺乳动物/长颈鹿/老虎每次都会把它送人。谢谢你的解释Eric!我是你博客的忠实读者。在我真正“理解”你的答案之前,我必须反复阅读几遍。请注意,正如Eric在“不安全参考元素数组协方差的细节”中警告的那样,C#的数组协方差特性被破坏了。如果您对协方差有正当的需求,但希望受到C#的类型安全系统的保护,请使用.Net 4.0+。这只花了我两个月的时间,但我终于明白为什么它不起作用了!
int[] x = new uint[10];
int[] x = (int[])(object) new uint[10];