ILの call と callvirt の違い その2
nullもガッと鳴いたよ。
using System; using System.Reflection.Emit; class Duck { public void Say() { Console.WriteLine( "ガッ" ); } } class Class3 { static void Main() { // void Do() メソッドを作る DynamicMethod dm = new DynamicMethod( "Do", null, null, typeof( Class3 ) ); ILGenerator ilgen = dm.GetILGenerator(); // null をスタックに push ilgen.Emit( OpCodes.Ldnull ); // Duck.Say() を呼ぶ ilgen.Emit( OpCodes.Call, typeof( Duck ).GetMethod( "Say" ) ); //ilgen.Emit( OpCodes.Callvirt, typeof( Duck ).GetMethod( "Say" ) ); ilgen.Emit( OpCodes.Ret ); // Do() を実行 dm.Invoke( null, null ); Console.ReadKey(); } }
ducktypingどころの話じゃなかった。Duckは一度も生成していない。
正確にはcallそのものはオブジェクト参照がスタックになくてもよい。(thisを必要としない)static callができるのでこれはまぁ当然。ただし、Duck.Say()は暗黙にthisを取るのでthisは必要。このコードではthisがnull。
対して、callvirtはオブジェクト参照が必要。必要といってもこっちもチェックしてるんだかどうなんだか。
↑のコードのcallをEmitしている行をコメントアウトし、callvirtをEmitしている行を活かすと内部例外がNullReferenceExceptionのTargetInvocationExceptionが出る。
パラメータチェックが甘いと解釈すべきなのかなぁ?