call と callvirt その5
5月の記事の続き。続きを書く日が来るとは自分でも驚き。IL のお話です。
インスタンスメソッドを呼ぶときにコンパイラが call を生成したり、callvirt を生成したりします。その違いは何よ?とずっと疑問だったんですが、その答えが!
vir http://blogs.msdn.com/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx
インスタンスの NULL チェックが不要なときは call だそうな。逆に NULL チェックを強制したいときが callvirt。NULL チェックの奇妙な機械語の件が The Old New Thing で取り上げられています。
NULL チェックが不要なときの例 : (new Foo()).FooNonVirtualMethod()
ちょっと実験。
using System; class Program { static void Main() { Hoge hoge = new Hoge(); Console.WriteLine( hoge.Huga() ); Console.WriteLine( new Hoge().Huga() ); } } public class Hoge { public string Huga() { return "HogeHuga"; } }
IL。Main だけ。
.method private hidebysig static void Main() cil managed { .entrypoint // コード サイズ 33 (0x21) .maxstack 1 .locals init ([0] class Hoge hoge) IL_0000: newobj instance void Hoge::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: callvirt instance string Hoge::Huga() IL_000c: call void [mscorlib]System.Console::WriteLine(string) IL_0011: newobj instance void Hoge::.ctor() IL_0016: call instance string Hoge::Huga() IL_001b: call void [mscorlib]System.Console::WriteLine(string) IL_0020: ret } // end of method Program::Main
IL_0007 は callvirt ですが、IL_0016 は確かに call になっています。
へぇなトリビアですね。
ちなみにエリックさんの同じエントリでC#コンパイラのバグでスレッドのデッドロックに至る可能性があるバグが告白されていたり。3.0でも健在だって。