为什么我的 C# 数组在转换为对象时会丢失类型符号信息?

2024-03-21

在调查一个错误时,我发现这是由于 c# 中的这种奇怪现象造成的:

sbyte[] foo = new sbyte[10];
object bar = foo;
Console.WriteLine("{0} {1} {2} {3}",
        foo is sbyte[], foo is byte[], bar is sbyte[], bar is byte[]);

输出是“True False True True”,而我期望的是“bar is byte[]" 返回 False。显然 bar 既是byte[] and an sbyte[]?对于其他有符号/无符号类型也会发生同样的情况,例如Int32[] vs UInt32[],但不是说Int32[] vs Int64[].

谁能解释这种行为?这是 .NET 3.5 中的内容。


更新:我使用这个问题作为博客条目的基础,在这里:

https://web.archive.org/web/20190203221115/https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/why-is-covariance-of-value-typed-arrays-inconcient/ https://web.archive.org/web/20190203221115/https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/why-is-covariance-of-value-typed-arrays-inconsistent/

有关此问题的详细讨论,请参阅博客评论。感谢您提出的好问题!


您偶然发现了 CLI 类型系统和 C# 类型系统之间一个有趣但不幸的不一致之处。

CLI 有“分配兼容性”的概念。如果已知数据类型 S 的值 x 与已知数据类型 T 的特定存储位置 y “赋值兼容”,则可以将 x 存储在 y 中。如果不是,那么这样做不是可验证的代码,验证者将禁止它。

例如,CLI 类型系统表示,引用类型的子类型与引用类型的超类型是赋值兼容的。如果你有一个字符串,你可以将它存储在对象类型的变量中,因为两者都是引用类型,并且字符串是对象的子类型。但反之则不然。超类型与子类型的赋值不兼容。如果不先进行强制转换,则无法将仅已知为对象的东西粘贴到字符串类型的变量中。

基本上“赋值兼容”意味着“将这些确切的位粘贴到这个变量中是有意义的”。从源值到目标变量的赋值必须是“表示保留”。有关详细信息,请参阅我的文章:

http://ericlippert.com/2009/03/03/representation-and-identity/ http://ericlippert.com/2009/03/03/representation-and-identity/

CLI 的规则之一是“如果 X 与 Y 的赋值兼容,则 X[] 与 Y[] 的赋值兼容”。

也就是说,数组在赋值兼容性方面是协变的。这实际上是一种破碎的协方差;有关详细信息,请参阅我的文章。

https://web.archive.org/web/20190118054040/https://blogs.msdn.microsoft.com/ericlippert/2007/10/17/covariance-and-contravariance-in-c-part-two-array-协方差/ https://web.archive.org/web/20190118054040/https://blogs.msdn.microsoft.com/ericlippert/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance/

这不是 C# 的规则。 C# 的数组协方差规则是“如果 X 是可隐式转换为引用类型 Y 的引用类型,则 X[] 也可隐式转换为 Y[]”。这是一个微妙的不同规则,因此你会遇到令人困惑的情况。

在 CLI 中,uint 和 int 是赋值兼容的。但在 C# 中,int 和 uint 之间的转换是 EXPLICIT,而不是 IMPLICIT,并且这些是值类型,而不是引用类型。所以在 C# 中,将 int[] 转换为 uint[] 是不合法的。

但它在 CLI 中是合法的。所以现在我们面临一个选择。

  1. 实现“is”,以便当编译器无法静态确定答案时,它实际上会调用一个方法来检查所有 C# 规则以确保身份保留可转换性。这很慢,并且 99.9% 的时间符合 CLR 规则。但我们会承受性能损失,以便 100% 符合 C# 规则。

  2. 实现“is”,以便当编译器无法静态确定答案时,它会执行令人难以置信的快速 CLR 分配兼容性检查,并接受这样一个事实:uint[] 是 int[],即使实际上这并不在 C# 中是合法的。

我们选择了后者。不幸的是,C# 和 CLI 规范在这个小问题上存在分歧,但我们愿意忍受这种不一致。

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

为什么我的 C# 数组在转换为对象时会丢失类型符号信息? 的相关文章

随机推荐