面白いことに、ToList を使用しているときにこの Linq キャストが失敗するのはなぜですか?
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) をスタックに直接ロードし、次にWriteLine
IL_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)
オプティマイザの動作に同意します。最適化フラグは、プログラムの動作を変更するべきではありません。