为什么在C#中“int []是uint [] == true”

时间:2009-02-27 06:27:56

标签: c# arrays casting unsigned

请有人澄清一下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#在调试器和编译器中引发了转换/转换错误,但在运行时却没有?

5 个答案:

答案 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:

这与上述有关。简而言之,因为涉及它的强制转换不会抛出异常(基于当前的溢出设置)。这是一个好主意是另一个问题。

From MSDN

  

如果提供的表达式为非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