无法将值类型数组转换为 params object[]

2023-11-21

如果 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");
    }
}

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

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

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

首先,我们应该清楚地说明“协方差”的含义。协方差是一个属性mapping保留了一个关系。这里的映射是“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();

因为哺乳动物是真的是一群长颈鹿.

这意味着每次你write到一个数组,其元素是未密封的引用类型,运行时执行类型检查如果类型检查失败可能会崩溃.

这是我认为的“C# 最差功能”,但事实上它确实如此work.

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

Because 这两件事在运行时有不同的形式。假设你有一个byte[]有十个元素。为数组元素保留的实际存储空间为十个字节长。假设您使用的是 64 位计算机,并且您有object[]有十个元素。存储空间扩大了八倍!

显然你无法转换通过引用转换对十个字节的存储的引用对十个八字节的存储的引用。额外的 70 个字节并不是凭空产生的;而是有的。必须有人分配它们。

而且:谁打拳击?如果您有一个包含十个对象的数组,并且每个对象都是一个字节,那么这些字节中的每一个都是boxed。但字节数组中的字节没有装箱。那么当你进行转换时,谁来进行拳击呢?

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

人们期望将一种数组类型转换为另一种数组类型不会分配并复制一个巨大的数组。但我们不能拥有参考身份介于十个字节的数组和包含十个引用的八十个字节的数组之间,因此整个事情都是非法的。

现在,你可能会说,好吧,当表征是相同的对于值类型?事实上,这在 C# 中是非法的:

int[] x = new uint[10];

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

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

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

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

  • 整个系列

  • 不安全参考元素数组协方差的细节

  • 有关值元素数组协方差的更多信息

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法将值类型数组转换为 params object[] 的相关文章

  • 使用 strcpy 从整数生成指针,无需进行强制转换

    我不明白我做错了什么 我正在学习 C 很抱歉 如果这显然是错误的 但我正在尝试使用uthash http uthash sourceforge net 制作股票及其价格的哈希图 但是当我将股票添加到哈希映射时 我收到上述错误 我所做的就是从
  • 确保 unsigned int/long 始终在 C# 中的检查上下文中执行

    有没有人觉得奇怪 uint 和 ulong 的默认上下文是未检查的 而不是检查的 因为它们旨在表示永远不能为负的值 因此 如果某些代码试图违反该约束 在我看来 自然且首选的行为是抛出异常 而不是返回最大值 这很容易使重要数据处于无效状态并且
  • Debug.WriteLine() 未命中

    我正在调试 Windows 服务 通过点击F5在 Visual Studio 2010 中 使用以下代码 In 程序 cs file static void Main if Environment UserInteractive We ar
  • .NET:EventHandler 竞争条件修复如何工作?

    以下模式用于在引发事件时避免竞争条件 以防另一个线程取消订阅 MyEvent 使其为空 class MyClass public event EventHandler MyEvent public void F EventHandler h
  • 从 C++ 中的函数返回二维数组[重复]

    这个问题在这里已经有答案了 可能的重复 C 从函数返回多维数组 https stackoverflow com questions 3716595 c returning multidimension array from function
  • 如何有效地左填充字节数组

    假设我有一个数组 LogoDataBy byte 0x00000008 0x00000000 0x41 0x00000001 0x42 0x00000002 0x43 0x00000003 0x44 0x00000004 0x31 0x00
  • 开始使用 TDD?

    我们正处于尝试实施 TDD 的初始阶段 我演示了 Visual Studio Team System 代码覆盖率 TDD 工具 团队对这种可能性感到兴奋 目前我们使用 Devpartner 进行代码覆盖 但我们希望消除它 因为它很昂贵 我们
  • 用于轻松动态反射的 C# 库

    是否有任何库 例如开源项目等 可以更轻松地使用复杂的反射 例如动态创建对象或类 检查实例等 Thanks 有一个LinFu http www codeproject com KB cs LinFuPart1 aspx可用的库除了反射之外还可
  • Apple IOS 上的 C# 应用程序 [已关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有基于 C Net 的应用程序 有什么方法可以在 Apple IOS 上运行这些应用程序吗 我没有资
  • 获取进程的所有 DLL

    我想获取为给定进程加载的所有 dll 的列表 我目前正在使用 NET框架4 0 我知道有一个bug https connect microsoft com VisualStudio feedback details 546430 syste
  • 如何使用 Linq to Sql 修剪值?

    在数据库中 我有一个名为 联系人 的表 名字和其他此类字符串字段设计为使用 Char 数据类型 不是我的数据库设计 我的对象 Contact 映射到属性中的字符串类型 如果我想做一个简单的测试 通过 id 检索 Contact 对象 我会这
  • 为什么这段代码不会产生编译错误?

    template
  • OpenMP 和 C++:this 指针

    Is thisOpenMP 中始终共享指针 尽管编译器不会抱怨以下代码default none pragma omp parallel for default none shared n for SInt i 0 i lt n i f i
  • 在 Ubuntu 16.04 上编译 PCL 1.7,CMake 生成的 Makefile 中出现错误

    我正在尝试让 PCL 1 7 点云库 而不是其他 pcl 在 Ubuntu 16 04 上运行 我最终希望用于 C 的东西 但现在我只是想让这些例子工作 我使用的是 Ubuntu GNU 5 3 1 附带的默认编译器和 Cmake 版本 3
  • AllowUserToAddRows 不适用于 DataGridView 上的 List<> 数据源

    我有一个DataGridView与DataSource set to List
  • 使用客户端 hello 消息进行 TLS 协议检测

    我需要检测网络流量中的 https 数据包 到目前为止 我将所有 443 标记为 https 但我不想再在这种情况下使用端口信息 检查客户端问候消息是否足够 Check 22 and version info 0300 0301 or 03
  • .NET 发布模式构建中是否提供堆栈跟踪信息?

    如果我选择发布模式来构建 dll 堆栈跟踪信息仍然可用吗 如果是的话 那么什么信息is发布模式下不可用 您始终拥有堆栈跟踪信息 这是与构建模式无关的运行时功能 但行号和源文件名通常在发布构建堆栈跟踪中不可用 您可以通过更改构建配置来创建完整
  • 如何重写(重新实现)QFileSystemModel 中的成员函数

    我已经为此苦苦挣扎了一段时间 Qt s QFileSystemModel由于图标获取算法非常糟糕 在获取数百个文件时速度非常慢 我想完全禁用图标 它们被提取到QFileSystemModel data方法不是虚拟的 QFileSystemM
  • Opencv 对象检测:ORB GPU 检测器和 SURF GPU 描述符提取器

    我只是做了一个小实验来尝试不同的检测器 描述符组合 我的代码使用 ORB GPU 检测器来检测特征 并使用 SURF GPU 描述符来计算描述符 我使用 BruteForceMatcher GPU 来匹配描述符 并使用 knnMatch 方
  • Eclipse CDT C/C++:包含另一个项目的头文件

    我在 Eclipse CDT 中有两个 C 项目main and shared In shared我有一个名为calc h 我想在中使用这个标头main 所以我做了以下事情 added include calc h到相关文件main In

随机推荐