やりたいことの意味では等価なコードが2つある。
private static void NewMethod()
{
object o = new object();
string s = o as string;
if (s != null)
{
string s2 = s;
}
}private static void NewMethod2()
{
object o = new object();
if (o is string)
{
string s2 = (string)o;
}
}
だがこの2つには大きな違いがある。
以下のILを比較してほしい。
.method private hidebysig static void NewMethod() cil managed
{
.maxstack 2
.locals init (
[0] object o,
[1] string s,
[2] string s2,
[3] bool CS$4$0000)
L_0000: nop
L_0001: newobj instance void [mscorlib]System.Object::.ctor()
L_0006: stloc.0
L_0007: ldloc.0
L_0008: isinst string
L_000d: stloc.1
L_000e: ldloc.1
L_000f: ldnull
L_0010: ceq
L_0012: stloc.3
L_0013: ldloc.3
L_0014: brtrue.s L_001a
L_0016: nop
L_0017: ldloc.1
L_0018: stloc.2
L_0019: nop
L_001a: ret
}
.method private hidebysig static void NewMethod2() cil managed
{
.maxstack 2
.locals init (
[0] object o,
[1] string s2,
[2] bool CS$4$0000)
L_0000: nop
L_0001: newobj instance void [mscorlib]System.Object::.ctor()
L_0006: stloc.0
L_0007: ldloc.0
L_0008: isinst string
L_000d: ldnull
L_000e: cgt.un
L_0010: ldc.i4.0
L_0011: ceq
L_0013: stloc.2
L_0014: ldloc.2
L_0015: brtrue.s L_0020
L_0017: nop
L_0018: ldloc.0
L_0019: castclass string
L_001e: stloc.1
L_001f: nop
L_0020: ret
}
NewMethod2になく、NewMethodにあるコードが1つある。
スタック宣言(s用)がそれだ。しかし、これはスタックなので大したことはない。
NewMethodになく、NewMethod2にあるコードが1つある。
ここにcastclassオプコードがある。
http://msdn2.microsoft.com/ja-jp/library/system.reflection.emit.opcodes.castclass(vs.80).aspx
ちなみにasもisも実体としてはisinstオプコードに変換されているので、ほぼ等価である。
http://msdn2.microsoft.com/ja-jp/library/system.reflection.emit.opcodes.isinst(vs.80).aspx
isinstの説明をよく読むと
class を実装している場合 (class がインターフェイスの場合)、または class の派生クラスの場合 (class が通常のクラスの場合) は、Castclass が呼び出された場合とまったく同様に、このクラスは class 型にキャストされ、その結果がスタックにプッシュされます。それ以外の場合は、null 参照がスタックにプッシュされます。オブジェクト参照自体が null 参照である場合は、isinst が同様に null 参照を返します。
castclassの説明には
新しいクラスの派生クラスでもない場合は、InvalidCastException がスローされます。
要はCastclassとおなじことをして、castclassでは変換できない場合にはInvalidCastExceptionを出すけど、nullがスタックにプッシュされるよと書かれています。
2回同じことをしているわけです。
このあたりはEssential .NETに詳しく書かれているので、そこを当たるとよい。動的に確保している継承ツリーを追っかけて、型変換可能かどうかを調べるので、遅い。
ちなみに例外がある。
C++/CLIでのstatic_cast()は、コード上になにも影響を与えない本当のキャストなので、パフォーマンス上の劣化はない。
このあたりは拙著 実践C++/CLIでも触れているので参照してほしい。