在C#/ .NET中,为什么sbyte []与byte []相同,除非它不是?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (16)

我刚刚在C#/ .NET中观察到一个奇怪的现象。

我创建了这个最小的例子来演示:

if (new sbyte[5] is byte[])
{
 throw new ApplicationException("Impossible!");
}

object o = new sbyte[5];

if (o is byte[])
{
 throw new ApplicationException("Why???");
}

这会抛出“为什么?”,但不是“不可能!”。它适用于所有相同大小的整型数组。谁可以给我解释一下这个?我很困惑。顺便说一句,我使用.NET 4。

我知道我可以通过使用获得预期的结果o.GetType() == typeof(byte[])

提问于
用户回答回答于

CLR的铸造规则指出这是可能的。C#规则说这是不可能的。C#团队有意识地决定,由于各种原因,他们会容忍这种偏离规范的情况。

为什么CLR允许这样做?可能是因为他们可以方便地实施它。bytesbyte具有相同的二进制表示形式,因此您可以将a byte[]视为“ sbyte[] 不会违反内存安全性”

相同的技巧适用于具有相同内存布局的其他基本类型。

用户回答回答于

我的问题是C#编译器,无论出于何种原因,他们认为它们永远不会是同一件事,并有助于将其优化为假。但是,CLR 的确让这种情况发生。对象转换抛出编译器优化,所以它通过CLR。

相关部分:

尽管在C#中不能直接将byte []转换为sbyte [],但CLR允许:

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[]);

“这种行为是否由Optimize Code标志(/o对编译器)控制?”

鉴于此代码:

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

并且用csc /o- Code.cs(不优化)进行编译,似乎无论如何编译器都会优化它。由此产生的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将0(false)直接加载到堆栈上,然后调用WriteLineIL_0009。所以不,优化标志没有什么区别。如果要咨询CLR,isinst指令将被使用。从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)

我会同意优化器的行为。优化标志不应改变程序的行为。

扫码关注云+社区