我有一个类似于以下代码的类:
public class MyClass
{
public int MyProperty1 { get; set; }
}
我希望创建一个动态的方法tostring
public static string MyProperty1ToString(MyClass o){
return o.MyProperty1.ToString();
}
MyProperty1ToString:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: callvirt UserQuery+MyClass.get_MyProperty1
IL_0007: stloc.0
IL_0008: ldloca.s 00
IL_000A: call System.Int32.ToString
IL_000F: stloc.1
IL_0010: br.s IL_0012
IL_0012: ldloc.1
IL_0013: ret
所以我尝试使用Emit创建方法,但出现错误
InvalidProgramException:公共语言运行库检测到无效程序
public class Program
{
public static void Main()
{
var obj = new MyClass() { MyProperty1 = 123 };
var prop = obj.GetType().GetProperty("MyProperty1");
var func = GetByPropertyCallToStringFunction<MyClass>(prop);
var data = func(obj); //InvalidProgramException : common language runtime detected an invalid program
}
public static Func<T, string> GetByPropertyCallToStringFunction<T>(PropertyInfo prop)
{
var type = prop.DeclaringType;
var propGetMethod = prop.GetMethod;
var propType = prop.PropertyType;
DynamicMethod dynamicMethod = new DynamicMethod($"{prop.Name}_method", typeof(string), new Type[] { type }, type.Module);
var toStringMethod = propType.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(p => p.Name == "ToString").First();
ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, propGetMethod);
il.Emit(OpCodes.Stloc);
il.Emit(OpCodes.Ldloca_S, 00);
il.Emit(OpCodes.Call, toStringMethod);
il.Emit(OpCodes.Stloc_1);
il.Emit(OpCodes.Br_S, "IL_0012");
il.Emit(OpCodes.Ldloc_1);
il.Emit(OpCodes.Ret);
var invoke = (Func<T, string>)dynamicMethod.CreateDelegate(typeof(Func<T, string>));
return invoke;
}
}
如何获取发出运行时编译器详细信息错误消息?
我不知道何时只有InvalidProgramException
答案 0 :(得分:3)
知道了。 除了必须明确声明分支标签(已正确发现ckuri)这一事实之外,您还必须在引用它们之前声明局部变量-包括它们的类型。
代码如下:
var propType = prop.PropertyType;
var propGetMethod = prop.GetMethod;
ILGenerator il = dynamicMethod.GetILGenerator();
LocalBuilder local0 = il.DeclareLocal(typeof(propType));
LocalBuilder local1 = il.DeclareLocal(typeof(string));
Label label0 = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, propGetMethod);
il.Emit(OpCodes.Stloc, local0);
il.Emit(OpCodes.Ldloca_S, local0);
il.Emit(OpCodes.Call, toStringMethod);
il.Emit(OpCodes.Stloc, local1);
il.Emit(OpCodes.Br_S, label0);
il.MarkLabel(label0);
il.Emit(OpCodes.Ldloc, local1);
il.Emit(OpCodes.Ret);
return (Func<T, string>)dynamicMethod.CreateDelegate(typeof(Func<T, string>));
但是,此版本的优化不是很好。看来您是从为调试模式编译的代码中摘录的。
以下版本具有相同功能,但效率更高,主要不需要标签,只需要一个本地标签即可。
var propType = prop.PropertyType;
var propGetMethod = prop.GetMethod;
ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
LocalBuilder local0 = iLGenerator.DeclareLocal(propType);
iLGenerator.Emit(OpCodes.Ldarg_0);
iLGenerator.Emit(OpCodes.Call, propGetMethod);
iLGenerator.Emit(OpCodes.Stloc, local0);
iLGenerator.Emit(OpCodes.Ldloca_S, local0);
iLGenerator.Emit(OpCodes.Call, toStringMethod);
iLGenerator.Emit(OpCodes.Ret);
return (Func<T, string>)dynamicMethod.CreateDelegate(typeof(Func<T, string>));