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

use*_*771 51 c#

我刚刚在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???");
}
Run Code Online (Sandbox Code Playgroud)

这将抛出"为什么???",但不是"不可能!".它适用于所有相同大小的整数类型的数组.谁可以给我解释一下这个?我糊涂了.我顺便使用.NET 4.

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

usr*_*usr 51

CLR的强制规则指出这是可能的.C#规则说这是不可能的.C#团队有意识地决定,出于各种原因,他们会容忍这种偏离规范的行为.

为什么CLR允许这样做?可能是因为他们可以方便地实现它.byte并且sbyte具有相同的二进制表示这样你就可以"治疗" byte[]作为sbyte[] 不违反内存安全.

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

  • 您应该包括这个偏差与值类型数组隔离的事实. (3认同)
  • 它也适用于ref类型(数组co/contravariance).Ref类型数组虽然有运行时检查.我引用Eric:CLI的一个规则是"如果X与Y兼容,那么X []赋值与Y []"兼容. (3认同)

vcs*_*nes 28

有趣的是,在我的问题中,我被它咬了,为什么使用ToList时这个Linq Cast会失败?

Jon Skeet(当然)解释说我的问题是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[]);
Run Code Online (Sandbox Code Playgroud)

Joel在评论中提出了一个有趣的问题:"这种行为是由Optimize Code标志控制的/o吗(对编译器来说)?"

鉴于此代码:

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

并使用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
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

我同意优化器的行为.优化标志不应更改程序的行为.