c# - 无法将值类型数组转换为 params 对象 []

标签 c# .net

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

简单程序示例:

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[]?



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

它们有点棘手,并且以几种有趣且不幸的方式被破坏。

首先,我们应该清楚地说明我们所说的“协方差”是什么意思。协方差是映射保留关系的属性。这里的映射是“T 到 T 的数组”。关系是“可以隐式转换”。例如:
Giraffe可以隐式转换为 Mammal .

这是两种类型之间的关系。现在将映射应用于关系的双方:
Giraffe[]可以转换为Mammal[] .

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

简而言之,与其说“从 T 到 T 数组的映射是隐式转换关系上的协变映射”,我们只是说“数组是协变的”,并希望其余部分可以从上下文中理解。

好的,现在我们有了定义:具有引用类型元素的数组在 C# 中是协变的。可悲的是,这是破坏协方差:
class Mammal {}
class Giraffe : Mammal {}
class Tiger : Mammal {}
...
Mammal[] mammals = new Giraffe[1];  

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

因为哺乳动物实际上是长颈鹿的阵列。

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

这是我对“C# 最差功能”的候选,但它确实有效。

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

因为这两个东西在运行时有不同的形式。假设你有一个 byte[]有十个元素。为数组元素保留的实际存储长度为 10 个字节。假设你在一台 64 位机器上并且你有一个 object[]有十个元素。存储空间大八倍!

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

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

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

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

现在,您可能会说,当值类型的表示相同时会发生什么?事实上,这在 C# 中是非法的:
int[] x = new uint[10];

因为在 C# 中,规则是只有仅涉及引用类型的协变数组转换才是合法的。但是如果你强制它由运行时完成:
int[] x = (int[])(object) new uint[10];

然后运行时允许它,因为四字节 int 和四字节 uint 具有相同的表示。

如果你想更好地理解这一点,那么你应该阅读我关于协方差和逆变如何在 C# 中工作的整个系列文章:
  • The whole series
  • The specifics of unsafe reference-element array covariance
  • More about value-element array covariance
  • 关于c# - 无法将值类型数组转换为 params 对象 [],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9104268/

    相关文章:

    c# - 无论如何修复 ASP.NET gridview 中的列宽

    c# - 绑定(bind) : Visualize a list of unnamed booleans using DataTemplate and bindings

    .net - 为什么可以使用反射从签名程序集中加载未签名程序集中的类型?

    c# - ListView 分组和排序

    c# - 以编程方式创建 MSTest 单元测试

    c# - Q : Does VS2015 Community Update 3 compile Windows UWP Apps for Win10 Build 10586 on Win7SP1?

    c# - 在 C# 中编码固定大小的枚举数组

    c# - 将欧拉转换为矩阵并将矩阵转换为欧拉

    c# - 从富文本框中获取选中的文本

    .net - 具有预编译 View 的助手