请有人澄清一下C#is
关键字。特别是这两个问题:
Q1)第5行;为什么这会回归真实?
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文章)
expression is not null. expression can be cast to type.
我不相信你可以对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#在调试器和编译器中引发了转换/转换错误,但在运行时却没有?
答案 0 :(得分:34)
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#新闻组的完整邮件。答案 1 :(得分:4)
现在这很有趣。我在ECMA-335标准中找到了这个。 4.3 castclass。请注意:
数组继承自System.Array。
如果可以将Foo强制转换为Bar,则可以将Foo []强制转换为Bar []。
出于上述注释2的目的,枚举被视为其基础类型:因此,如果E1和E2共享基础类型,则E1 []可以转换为E2 []。
你可以将int强制转换为uint,但它的表现非常奇怪。连接调试器时,Visual Studio无法识别任何此类内容,甚至是手表,只显示问号“?”。
你可能想看看this,快进大约10分钟,然后听Anders解释共变阵列的实现。我认为这是根本上的根本问题。
答案 2 :(得分:1)
建议:
将intArray声明为“int [] intArray”而不是“object intArray”将允许编译器获取无效的C#强制转换。除非你绝对必须使用对象,否则我会采用这种方法。
Re Q2,Q3:
在运行时,您是否尝试将转换包装在checked块中?
来自MSDN上的这篇文章:
默认情况下,表达式 仅包含常量值导致a 如果表达式编译错误 产生一个超出的值 目的地类型的范围。如果 表达式包含一个或多个 非常量值,编译器 没有检测到溢出。
...
默认情况下,这些非常量 不检查表达式 在运行时溢出,他们 不要引发溢出异常。该 上一个示例显示 -2,147,483,639为两个正整数的总和。
可以通过启用溢出检查 编译器选项,环境 配置,或使用已检查 关键字。
正如它所说,您可以通过编译器设置或环境配置更全面地强制执行溢出检查。
在你的情况下,这可能是合乎需要的,因为它会导致抛出运行时错误,这将确保可能无效的无符号数到符号数溢出不会以静默方式发生。
[更新]测试此代码后,我发现使用object类型的声明而不是int []似乎绕过标准的C#强制sytax,无论是否启用了checked。
正如JS所说,当你使用对象时,你受到CLI规则的约束,这些显然允许这种情况发生。
Re Q1:
这与上述有关。简而言之,因为涉及它的强制转换不会抛出异常(基于当前的溢出设置)。这是一个好主意是另一个问题。
如果提供的表达式为非null,则“is”表达式的计算结果为true 提供的对象可以转换为提供的类型,而不会导致异常 抛出。
答案 3 :(得分:0)
我猜测与.NET 1的向后兼容性:我对细节仍然有些模糊,但我相信所有数组的CLR类型只是System.Array,具有额外的Type属性来查找元素类型。 '是'可能只是没有考虑到CLR v1中的那个,现在必须保持这一点。
它不适用于(uint[])(new int[]{})
情况可能是由于C#编译器(而不是CLR运行时)能够进行更严格的类型检查。
此外,数组通常只是类型不安全:
Animal[] tigers = new Tiger[10];
tigers[3] = new Elephant(); // ArrayTypeMismatchException
答案 4 :(得分:0)
行,
我试图捅这个。
首先,文档说,“检查对象是否与给定类型兼容。”,它还说如果左侧的类型是“可投射的”(您可以毫无例外地转换)到类型上对,并且表达式求值为非null,“is”关键字将计算为true
。
向Jon Skeet寻找其他答案。他说这比我说的更有说服力。他是对的,如果转换可用,它会接受你的语法,你可以随后编写自己的语法,但在这种情况下看起来有点矫枉过正。
参考:http://msdn.microsoft.com/en-us/library/scekt9xw(VS.80).aspx