为啥 C# 中的“int[] 是 uint[] == true”
Posted
技术标签:
【中文标题】为啥 C# 中的“int[] 是 uint[] == true”【英文标题】:Why does "int[] is uint[] == true" in C#为什么 C# 中的“int[] 是 uint[] == true” 【发布时间】:2010-10-10 06:33:51 【问题描述】:请有人澄清 C# is
关键字。特别是这两个问题:
Q1) 第 5 行;为什么返回 true?
Q2) 第 7 行;为什么没有强制转换异常?
public void Test()
object intArray = new int[] -100, -200 ;
if (intArray is uint[]) //why does this return true?
uint[] uintArray = (uint[])intArray; //why no class cast exception?
for (int x = 0; x < uintArray.Length; x++)
Console.Out.WriteLine(uintArray[x]);
MSDN 的描述没有说明情况。它声明is
将在满足其中任何一个条件时返回 true。 (http://msdn.microsoft.com/en-us/library/scekt9xw(VS.71).aspx>MDSN 文章)
我不相信您可以将 int[] 有效地转换为 uint[]。因为:
A) 此代码无法编译:
int[] signed = new int[] -100 ;
uint[] unsigned = (uint[])signed;
B) 在调试器中进行强制转换会出错:
(uint[])signed
"Cannot convert type 'int[]' to 'uint[]'"
果然,如果第 3 行是 int[] 而不是 object 那么它永远不会编译。这让我想到了与 Q2 相关的最后一个问题。
Q3) 为什么 C# 会在调试器和编译器中引发转换/转换错误,但在运行时不会?
【问题讨论】:
当你需要 Eric Lippert 时他在哪里? 错了,这是 Stack Overflow,你的意思是“当你需要 Jon Skeet 时他在哪里?” ;) 检查我的答案,我更新了一个视频链接,我认为该视频解释了潜在问题。看看吧! 埃里克·利珀特在这里:why-does-my-c-sharp-array-lose-type-sign-information-when-cast-to-object 【参考方案1】:C# 和 CLR 的转换规则有些不同。
您不能直接在 C# 中在 int[]
和 uint[]
之间进行转换,因为 语言 不相信任何转换可用。但是,如果您通过 object
访问,则结果取决于 CLI。来自 CLI 规范第 8.7 节(我希望 - 我刚才引用了 email exchange I had on this topic with Eric Lippert):
有符号和无符号整数原语 类型可以相互分配; 例如, int8 := uint8 是有效的。为了这 目的,应考虑 bool 与
uint8
兼容,反之亦然, 这使得bool := uint8
有效,并且 反之亦然。这也适用于 有符号和无符号整数数组 相同大小的原始类型; 例如,int32[] := uint32[]
是有效的。
(我没有检查过,但我认为这种引用类型转换是有效的,这也是 is
返回 true 的原因。)
不幸的是,语言与底层执行引擎之间存在脱节,但我怀疑从长远来看,这几乎是不可避免的。还有一些类似的案例,但好消息是它们似乎很少造成重大伤害。
编辑:由于 Marc 删除了他的答案,我已链接到 Eric 的完整邮件,该邮件已发布到 C# 新闻组。
【讨论】:
听起来像是“你可以在 MSIL 中做什么而在 c# 中不能”问题的另一个候选人。直接转换,无需使用对象/数组变量来安抚 c# 编译器 @ShuggyCoUk:好电话 - 将更新我的答案,链接到这个问题。 对我来说听起来不太安全。【参考方案2】:现在这很有趣。我在 ECMA-335 标准中发现了这一点。 4.3 演员表。请注意:
数组继承自 System.Array。
如果 Foo 可以转换为 Bar,则 Foo[] 可以转换为 Bar[]。
出于上述注释 2 的目的,枚举被视为其基础类型:因此,如果 E1 和 E2 共享基础类型,则 E1[] 可以强制转换为 E2[]。
您可以将 int 转换为 uint,但它的行为方式非常奇怪。 Visual Studio 无法识别任何这些,即使是手表,当附加调试器时只会显示一个问号“?”。
你可能想看看this,快进大约 10 分钟,然后听安德斯解释协变数组的实现。我认为这是根本的根本问题。
【讨论】:
【参考方案3】:建议:
将 intArray 声明为“int [] intArray”而不是“object intArray”将允许编译器拾取无效的 C# 转换。除非您绝对必须使用对象,否则我会采用这种方法。
关于第二季度,第三季度:
在运行时,您是否尝试过将演员表包装在 checked 块中?
来自 MSDN 上的这篇文章:
默认情况下,一个表达式 仅包含常量值会导致 如果表达式编译器错误 产生一个超出范围的值 目标类型的范围。如果 表达式包含一个或多个 非常量值,编译器会 没有检测到溢出。
...
默认情况下,这些非常量 不检查表达式 在运行时溢出,它们 不要引发溢出异常。这 上一个示例显示 -2,147,483,639 为两个正整数之和。
溢出检查可以通过 编译器选项,环境 配置,或使用检查 关键字。
正如它所说,您可以通过编译器设置或环境配置在全局范围内强制执行溢出检查。
在您的情况下,这可能是可取的,因为它会导致抛出运行时错误,这将确保可能的无效无符号数到有符号数溢出不会静默发生。
[更新] 在测试这段代码后,我发现使用 object 类型的声明而不是 int [] 似乎绕过了标准的 C# 转换语法,无论是否启用了检查。
正如 JS 所说,当你使用对象时,你会受到 CLI 规则的约束,而这些显然允许这种情况发生。
关于 Q1:
这与上述有关。简而言之,因为涉及的演员阵容不会引发异常(基于当前的溢出设置)。这是否是一个好主意是另一个问题。
From MSDN:
如果提供的表达式不为空,则“is”表达式的计算结果为 true,并且 提供的对象可以转换为提供的类型而不会导致异常 扔了。
【讨论】:
但它并没有回答他的第一个问题,“为什么 (intArray is uint[])” 不,令人惊讶的是,检查并没有发现这一点。调试器完全被内容弄糊涂了(只显示 ? 用于检查器中的元素),并且取出值得到包装的值。 我的测试中的行为相同。当你需要乔恩·斯基特时,他在哪里? 在那儿。 (可能很快就会出现:)【参考方案4】:我猜测与 .NET 1 的向后兼容性:我对细节仍然有些模糊,但我相信所有数组的 CLR 类型都只是 System.Array,具有额外的 Type 属性来查找元素类型。 'is' 可能只是在 CLR v1 中没有考虑到这一点,现在必须保持这一点。
它在 (uint[])(new int[])
的情况下不起作用可能是因为 C# 编译器(而不是 CLR 运行时)能够进行更严格的类型检查。
此外,数组通常只是类型不安全的:
Animal[] tigers = new Tiger[10];
tigers[3] = new Elephant(); // ArrayTypeMismatchException
【讨论】:
【参考方案5】:好的,
我试图对此进行尝试。
首先,文档说,“检查一个对象是否与给定类型兼容。”,它还说如果左边的类型是“可转换的”(你可以毫无例外地转换)到对,并且表达式的计算结果为非 null,“is”关键字的计算结果为 true
。
请向 Jon Skeet 寻求其他答案。他说得比我更有说服力。他是对的,如果转换可用,它将接受您的语法,您可以随后编写自己的语法,但在这种情况下似乎有点矫枉过正。
参考:http://msdn.microsoft.com/en-us/library/scekt9xw(VS.80).aspx
【讨论】:
以上是关于为啥 C# 中的“int[] 是 uint[] == true”的主要内容,如果未能解决你的问题,请参考以下文章
仅在 C# 中的 Date 类型 - 为啥没有 Date 类型?