[英]In C#/.NET why is sbyte[] the same as byte[] except that it's not?
我剛剛在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。
PS:我知道我可以通過使用o.GetType() == typeof(byte[])
來獲得預期的結果。
CLR的強制規則指出這是可能的。 C#規則說這是不可能的。 C#團隊有意識地決定 ,出於各種原因,他們會容忍這種偏離規范的行為。
為什么CLR允許這樣做? 可能是因為他們可以方便地實現它。 byte
和sbyte
具有相同的二進制表示,因此您可以將byte[]
視為sbyte[]
而不會違反內存安全性 。
同樣的技巧適用於具有相同內存布局的其他基本類型。
有趣的是,在我的問題中,我被它咬了, 為什么使用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[]);
Joel在評論中提出了一個有趣的問題,“這種行為是否由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)直接加載到堆棧上,然后在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實際上在編譯時“拋出”:
類型'SByte'的1維陣列的表達永遠不能是'Byte'的1維陣列。
相當於第一個if
語句。
並且等效於第二個if
成功(即它拋出編碼異常)在運行時如預期的那樣,因為它是相同的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
表達式的兩邊靜態類型為元素類型為有符號或無符號整數或枚舉的數組時才會出現,並且整數的大小相同。
好消息是,雖然這可能看起來不一致,但C#會在將false
替換為這樣的表達式時發出警告 - 實際上,我認為這可能比靜默返回true
更有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.