在C#/ .NET中,为什么sbyte 与byte 相同,除非它不是?
我只是在C#/。NET中观察到一个奇怪的现象。
我创build了这个最小的例子来演示:
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。
PS:我知道我可以通过使用o.GetType() == typeof(byte[])
来获得预期的结果。
CLR的铸造规则指定这是可能的。 C#规则说这是不可能的。 C#团队有意识地决定 ,由于种种原因,他们会容忍这种偏离规范的行为。
为什么CLR允许这样做? 可能是因为他们可以方便地实施它。 byte
和sbyte
具有相同的二进制表示forms,因此您可以在不影响内存安全的情况下将byte[]
视为sbyte[]
。
相同的技巧适用于具有相同内存布局的其他基本types。
有趣的是,我在我的问题中被咬了, 为什么Linq在使用ToList时失败?
Jon Skeet (当然)解释说,我的问题是C#编译器,无论出于何种原因,认为它们永远不可能是同一个东西,并有助于将其优化为false。 但是,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[]);
Joel在评论中问了一个有趣的问题:“这个行为是由优化代码标志( /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)加载到堆栈上,然后在IL_0009上调用WriteLine
。 所以不,优化标志没有什么区别。 如果要咨询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)
我会同意优化器的行为。 优化标志不应该改变你的程序的行为。
VB.NET实际上在编译时“抛出”:
types“SByte的一维数组”的expression不能是types“字节的一维数组”。
相当于第一个if
语句。
if
成功(如它抛出编码的exception)的第二个等价物如预期的那样在运行时,因为它是相同的CLR。
这是一个简单的例子,显示了同样的问题:
static void Main(string[] args) { bool a = ((object) new byte[0]) is sbyte[]; bool b = (new byte[0]) is sbyte[]; Console.WriteLine(a == b); // False }
不一致的出现是因为C#编译器决定在编译时知道(new byte[0]) is sbyte[]
,只是用false
代替。 也许它应该真正代替true
,以更符合CLR的行为。
据我所知,只是这个小小的优化是不一致的。 只有当is
expression式的两边静态types为一个数组,其元素types是一个有符号或无符号整数或一个枚举,整数的大小是相同的。
好消息是虽然这看起来可能不一致,但是C#在将false
换成这样的expression式时总会发出警告 – 实际上,我认为这可能比悄悄地返回true
更有用。