I am very confused ... you see: this code should not work, since you actually did not allocate any local residents; for example, here is a poorly written (in that it uses unnecessary locals) multiply-by-4 method, which does not declare locals:
var method = new DynamicMethod("MulBy4", typeof (int), new Type[] {typeof (int)}); var il = method.GetILGenerator(); il.Emit(OpCodes.Ldc_I4_4); il.Emit(OpCodes.Stloc_0); // this usage is il.Emit(OpCodes.Ldloc_0); // deliberately silly il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Stloc_1); // this usage is il.Emit(OpCodes.Ldloc_1); // deliberately silly il.Emit(OpCodes.Ret); var mulBy4= (Func<int,int>)method.CreateDelegate(typeof (Func<int, int>)); var twelve = mulBy4(3);
This throws a VerificationException :
An operation can destabilize runtime.
since it is not checked. This is bad il! If we change it to:
var method = new DynamicMethod("MulBy4", typeof (int), new Type[] {typeof (int)}); var il = method.GetILGenerator(); il.DeclareLocal(typeof (int)); il.DeclareLocal(typeof(int)); ...
then it works now . This leads to an alternative to storing numbers - by storing and using the LocalBuilder returned from DeclareLocal :
var method = new DynamicMethod("MulBy4", typeof (int), new Type[] {typeof (int)}); var il = method.GetILGenerator(); var multiplier = il.DeclareLocal(typeof (int)); var result = il.DeclareLocal(typeof(int)); il.Emit(OpCodes.Ldc_I4_4); il.Emit(OpCodes.Stloc, multiplier); il.Emit(OpCodes.Ldloc, multiplier); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Mul); il.Emit(OpCodes.Stloc, result); il.Emit(OpCodes.Ldloc, result); il.Emit(OpCodes.Ret); var mulBy4= (Func<int,int>)method.CreateDelegate(typeof (Func<int, int>)); var twelve = mulBy4(3);
If you are concerned that this uses a longer version of IL, you can use instead:
static void LoadLocal(this ILGenerator il, LocalBuilder local) { switch(local.LocalIndex) { case 0: il.Emit(OpCodes.Ldloc_0); break; case 1: il.Emit(OpCodes.Ldloc_1); break; case 2: il.Emit(OpCodes.Ldloc_2); break; case 3: il.Emit(OpCodes.Ldloc_3); break; default: if(local.LocalIndex < 256) { il.Emit(OpCodes.Ldloc_S, (byte) local.LocalIndex); } else { il.Emit(OpCodes.Ldloc, (ushort) local.LocalIndex); } break; } }
together with il.LoadLocal(multiplier); and il.LoadLocal(result); (and obviously something similar for Stloc )