In C#/.NET why is sbyte[] the same as byte[] except that it's not?

前端 未结 4 865
悲哀的现实
悲哀的现实 2020-12-08 04:05

I just observed a weird phenomenon in C#/.NET.

I created this minimal example to demonstrate:

if (new sbyte[5] is byte[])
{
 throw new ApplicationExc         


        
4条回答
  •  误落风尘
    2020-12-08 04:24

    Funny, I got bitten by that in my question, Why does this Linq Cast Fail when using ToList?

    Jon Skeet (of course) explains that my problem is the C# compiler, for whatever reason, thinks they could never be the same thing, and helpfully optimizes it to false. However, the CLR does let this happen. The cast to object throws off the compiler optimization, so it goes through the CLR.

    The relevant part from his answer:

    Even though in C# you can't cast a byte[] to an sbyte[] directly, the CLR allows it:

    var foo = new byte[] {246, 127};
    // This produces a warning at compile-time, and the C# compiler "optimizes"
    // to the constant "false"
    Console.WriteLine(foo is sbyte[]);
    
    object x = foo;
    // Using object fools the C# compiler into really consulting the CLR... which
    // allows the conversion, so this prints True
    Console.WriteLine(x is sbyte[]);
    

    Joel asked an interesting question in the comments, "Is this behavior controlled by the Optimize Code flag (/o to the compiler)?"

    Given this code:

    static void Main(string[] args)
    {
        sbyte[] baz = new sbyte[0];
        Console.WriteLine(baz is byte[]);
    }
    

    And compiled with csc /o- Code.cs (don't optimize), it appears that the compiler optimizes it anyway. The resulting IL:

    IL_0000:  nop
    IL_0001:  ldc.i4.0
    IL_0002:  newarr     [mscorlib]System.SByte
    IL_0007:  stloc.0
    IL_0008:  ldc.i4.0
    IL_0009:  call       void [mscorlib]System.Console::WriteLine(bool)
    IL_000e:  nop
    IL_000f:  ret
    

    IL_0008 loads 0 (false) directly onto the stack, then calls WriteLine on IL_0009. So no, the optimization flag does not make a difference. If the CLR were to be consulted, the isinst instruction would get used. It would probably look something like this starting from IL_0008:

    IL_0008:  ldloc.0
    IL_0009:  isinst     uint8[]
    IL_000e:  ldnull
    IL_000f:  cgt.un
    IL_0011:  call       void [mscorlib]System.Console::WriteLine(bool)
    

    I would agree with the optimizer's behavior. The optimization flag should not change the behavior of your program.

提交回复
热议问题