为什么我的 C# 数组在转换为对象时会丢失类型符号信息? [英] Why does my C# array lose type sign information when cast to object?

查看:39
本文介绍了为什么我的 C# 数组在转换为对象时会丢失类型符号信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

调查一个错误,我发现这是由于 c# 中的这种奇怪之处:<​​/p>

Investigating a bug, I discovered it was due to this weirdness in 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[] 又是 sbyte[]?对于其他有符号/无符号类型,例如 Int32[]UInt32[],也会发生同样的情况,但对于 Int32[] 而言并非如此Int64[].

The output is "True False True True", while I would have expected "bar is byte[]" to return False. Apparently bar is both a byte[] and an sbyte[]? The same happens for other signed/unsigned types like Int32[] vs UInt32[], but not for say Int32[] vs Int64[].

谁能解释一下这种行为?这是在 .NET 3.5 中.

Can anyone explain this behavior? This is in .NET 3.5.

推荐答案

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

UPDATE: I've used this question as the basis for a blog entry, here:

https://web.archive.org/web/20190203221115/https://blogs.msdn.microsoft.com/ericlippert/2009/09/24/为什么值的协方差类型化数组不一致/

有关此问题的扩展讨论,请参阅博客评论.谢谢你的好问题!

See the blog comments for an extended discussion of this issue. Thanks for the great question!

您偶然发现了 CLI 类型系统和 C# 类型系统之间有趣且不幸的不一致.

You have stumbled across an interesting and unfortunate inconsistency between the CLI type system and the C# type system.

CLI 具有分配兼容性"的概念.如果已知数据类型 S 的值 x 是赋值兼容的"使用已知数据类型 T 的特定存储位置 y,则可以将 x 存储在 y 中.如果不是,那么这样做不是可验证代码,验证者将不允许这样做.

The CLI has the concept of "assignment compatibility". If a value x of known data type S is "assignment compatible" with a particular storage location y of known data type T, then you can store x in y. If not, then doing so is not verifiable code and the verifier will disallow it.

例如,CLI 类型系统说,引用类型的子类型与引用类型的超类型赋值兼容.如果你有一个字符串,你可以将它存储在一个 object 类型的变量中,因为两者都是引用类型,而 string 是 object 的子类型.但事实并非如此.超类型与子类型的赋值不兼容.你不能在不先强制转换的情况下将只知道是对象的东西粘贴到字符串类型的变量中.

The CLI type system says, for instance, that subtypes of reference type are assignment compatible with supertypes of reference type. If you have a string, you can store it in a variable of type object, because both are reference types and string is a subtype of object. But the opposite is not true; supertypes are not assignment compatible with subtypes. You can't stick something only known to be object into a variable of type string without first casting it.

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

Basically "assignment compatible" means "it makes sense to stick these exact bits into this variable". The assignment from source value to target variable has to be "representation preserving". See my article on that for details:

http://ericlippert.com/2009/03/03/表征与身份/

CLI 的规则之一是如果 X 与 Y 的赋值兼容,则 X[] 与 Y[] 的赋值兼容".

One of the rules of the CLI is "if X is assignment compatible with Y, then X[] is assignment compatible with Y[]".

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

That is, arrays are covariant with respect to assignment compatibility. This is actually a broken kind of covariance; see my article on that for details.

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[]".这是一个微妙的不同规则,因此您的情况令人困惑.

That is NOT a rule of C#. C#'s array covariance rule is "if X is a reference type implicitly convertible to reference type Y, then X[] is implicitly convertible to Y[]". That is a subtly different rule, and hence your confusing situation.

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

In the CLI, uint and int are assignment compatible. But in C#, the conversion between int and uint is EXPLICIT, not IMPLICIT, and these are value types, not reference types. So in C#, it's not legal to convert an int[] to a uint[].

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

But it IS legal in the CLI. So now we are faced with a choice.

  1. 实现is"这样当编译器无法静态确定答案时,它实际上会调用一个方法,该方法检查所有 C# 规则以确保身份保留可转换性.这很慢,并且 99.9% 的时间与 CLR 规则相匹配.但是我们采取性能损失是为了 100% 符合 C# 的规则.

  1. Implement "is" so that when the compiler cannot determine the answer statically, it actually calls a method which checks all the C# rules for identity-preserving convertibility. This is slow, and 99.9% of the time matches what the CLR rules are. But we take the performance hit so as to be 100% compliant with the rules of C#.

实现is"因此,当编译器无法静态确定答案时,它会执行非常快的 CLR 分配兼容性检查,并接受这样一个事实,即 uint[] 是一个 int[],即使这在 C# 中实际上并不合法.

Implement "is" so that when the compiler cannot determine the answer statically, it does the incredibly fast CLR assignment compatibility check, and live with the fact that this says that a uint[] is an int[], even though that would not actually be legal in C#.

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

We chose the latter. It is unfortunate that C# and the CLI specifications disagree on this minor point but we are willing to live with the inconsistency.

这篇关于为什么我的 C# 数组在转换为对象时会丢失类型符号信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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