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

有人可以澄清C# is关键字请。 特别是这2个问题:

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 Article)

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 []而不是对象,那么它永远不会编译。 这给我带来了与Q2相关的最后一个问题。

Q3)为什么C#在调试器和编译器中引发了转换/转换错误,而不是在运行时呢?


C#和CLR有不同的转换规则。

您不能在C#中的int[]uint[]之间直接进行转换,因为该语言不相信任何转换可用。 但是,如果通过object则结果取决于CLI。 从CLI规范第8.7节(我希望 - 我引用了Eric Lippert前一段关于此主题的电子邮件交换):

有符号和无符号整数基元类型可以相互分配; 例如,int8:= uint8是有效的。 为此,bool应被认为与uint8兼容,反之亦然,这使得bool := uint8有效,反之亦然。 对于相同大小的带符号和无符号整数基元类型的数组也是如此; 例如, int32[] := uint32[]有效。

(我没有检查,但我认为这种引用类型转换为有效的是什么使is恢复也是如此。)

有些不幸的是,语言和底层执行引擎之间存在脱节,但从长远来看,这是不可避免的,我怀疑。 还有其他一些类似的情况,但好消息是它们似乎很少造成重大伤害。

编辑:当Marc删除他的答案时,我已经链接到来自Eric的完整邮件,发布到C#新闻组。


现在很有趣。 我在ECMA-335标准中发现了这一点。 4.3 castclass。 注意:

  • 数组继承自System.Array。

  • 如果Foo可以投射到Bar,那么Foo []可以投射到Bar []。

  • 出于上述注2的目的,枚举被视为其基础类型:因此,如果E1和E2共享一个基础类型,则E1 []可以转换为E2 []。

  • 你可以将int转换为uint,但它的行为非常奇怪。 当连接调试器时,Visual Studio不会识别出任何这一点,甚至是手表,只会显示一个问号'?'。

    你可能想看看这个,快进10分钟左右,并听取安德斯解释共变阵列实现。 我认为这是这里的根本问题。


    建议:

    将intArray声明为“int [] intArray”而不是“object intArray”将允许编译器获取无效的C#类型转换。 除非你绝对必须使用对象,否则我会采取这种方法。

    Re Q2,Q3:

    在运行时,你是否尝试在一个选中的块中包装这个转换?

    从MSDN的这篇文章:

    默认情况下,如果表达式生成的值超出目标类型的范围,则只包含常量值的表达式会导致编译器错误。 如果表达式包含一个或多个非常量值,则编译器不会检测到溢出。

    ...

    默认情况下,这些非常量表达式在运行时不会检查溢出,也不会引发溢出异常。 前面的示例显示-2,147,483,639为两个正整数的和。

    溢出检查可以通过编译器选项,环境配置或使用checked关键字来启用。

    正如它所说的,您可以通过编译器设置或环境配置来强制执行全局溢出检查。

    在你的情况这可能是可取的,因为它会导致运行时错误被抛出,这将确保可能无效的无符号数字签名数字溢出不会默默地发生。

    [更新]在测试了这段代码之后,我发现使用类型对象的声明而不是int []似乎绕过了标准的C#铸造sytax,无论是否启用了checked。

    正如JS所说的,当你使用对象时,你受CLI规则的约束,并且这些规则显然允许发生这种情况。

    Re Q1:

    这与上述有关。 总之,因为涉及到的投射不会抛出异常(基于当前的溢出设置)。 这是否是一个好主意是另一个问题。

    来自MSDN:

    如果提供的表达式非空,则“is”表达式的计算结果为true,并且提供的对象可以转换为提供的类型而不会引发异常。

    链接地址: http://www.djcxy.com/p/80655.html

    上一篇: Why does "int[] is uint[] == true" in C#

    下一篇: Make GSON deserialize numbers to integers or doubles