IL 同じミスはもうしたくないから拡張メソッドでお茶を濁す
あいさつ
どうも、はかせです。
前回ILでループ処理を作ろうとしたら
まさかのstaticメソッドとインスタンスメソッドを間違うという
ポカをやらかしてしまいました(´;ω;`)
もう同じ過ちを繰り返したくないので、
今回は引数のEmitを拡張メソッドにしてしまいたいと思います。
やること
今回の実装はシンプルイズベストということで
メソッドがstaticか調べて、
staticの場合はldarg.0
そうでないならldarg.1をEmitするという形にします。
ではさっそくコードです。
using System.Reflection; using System.Reflection.Emit; namespace ConsoleApp6 { static class ILUtility { /// <summary> /// 第一引数を取得 /// </summary> /// <param name="il"></param> /// <param name="method"></param> public static void LdargFirst(this ILGenerator il,MethodBase method) { //staticメソッドならldarg.0 if (method.IsStatic) il.Emit(OpCodes.Ldarg_0); //インスタンスメソッドならldarg.1 else il.Emit(OpCodes.Ldarg_1); } /// <summary> /// 指定した引数を取得 /// 使うかわからんけど思いついたから作ってみた /// </summary> /// <param name="il"></param> /// <param name="method"></param> /// <param name="index"></param> public static void Ldarg(this ILGenerator il, MethodBase method,int index) { //インスタンスメソッドなら第一引数はthisのため一個ずらす if (!method.IsStatic) index++; il.Emit(OpCodes.Ldarg, index); } } }
メソッドがstaticかどうかはMethodBaseのIsStaticプロパティで取得できます。
MethodBaseってのはその名の通り
MethodInfoとかMethodBuilderとかの基底クラスです。
あとはそのプロパティでif文分岐して
それぞれEmitしていきます。
ではこれを実際に使ったIL生成のコードがこちら
//Hoge型からSumインスタンスメソッドを定義 //第一引数にメソッド名を、第二引数にはアクセス修飾子、第三引数は戻り値、第四引数はメソッドの引数 var sum = typeBuilder.DefineMethod("Sum", MethodAttributes.Public, typeof(int), new Type[] { typeof(int[]) }); // IL手書きはいつもどおり var il = sum.GetILGenerator(); //ジャンプ先のラベルを宣言 var startLabel = il.DefineLabel(); var endLabel = il.DefineLabel(); //ローカル変数宣言 var iLoacl = il.DeclareLocal(typeof(int)); var iLoacl2 = il.DeclareLocal(typeof(int)); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Br_S, startLabel); il.MarkLabel(endLabel); il.Emit(OpCodes.Ldloc_0); //il.Emit(OpCodes.Ldarg_1)←これを拡張メソッドに置換 il.LdargFirst(sum); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldelem_I4); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloc_1); il.Emit(OpCodes.Ldc_I4_1); il.Emit(OpCodes.Add); il.Emit(OpCodes.Stloc_1); il.MarkLabel(startLabel); il.Emit(OpCodes.Ldloc_1); //il.Emit(OpCodes.Ldarg_1)←これを拡張メソッドに置換 il.LdargFirst(sum); il.Emit(OpCodes.Ldlen); il.Emit(OpCodes.Conv_I4); il.Emit(OpCodes.Blt_S, endLabel); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret);
これでstaticメソッドでもインスタンスメソッドでも
引数を間違うというポカはやらかさない!・・・はず
あとがき
今回はIL手書きでミスったことを反省し、
二度と起こさないよう拡張メソッドを作った話でした。
今回はLdargだけやりましたけど、
CallとかBr_sとかも拡張メソッドでくるんでやるのが
安全なんでしょうね。
それでは今回はこの辺でノシ