C# Emit 创建动态 属性 ToString 方法
C# Emit create dynamic property ToString method
我有一个 class 像下面的代码:
public class MyClass
{
public int MyProperty1 { get; set; }
}
我希望创建一个动态方法调用到字符串
public static string MyProperty1ToString(MyClass o){
return o.MyProperty1.ToString();
}
IL
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 : common language runtime detected an invalid program
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
测试Link:C# Emit to create dynamic Property ToString method | C# Online Compiler | .NET Fiddle
知道了。
除了必须显式声明分支标签(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>));
不过这个版本并没有优化太多。您似乎是从为调试模式编译的代码中获取的。
下面的版本也是一样的,但是效率更高,主要是不需要标签,只需要一个local:
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>));
我有一个 class 像下面的代码:
public class MyClass
{
public int MyProperty1 { get; set; }
}
我希望创建一个动态方法调用到字符串
public static string MyProperty1ToString(MyClass o){
return o.MyProperty1.ToString();
}
IL
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 : common language runtime detected an invalid program
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
测试Link:C# Emit to create dynamic Property ToString method | C# Online Compiler | .NET Fiddle
知道了。 除了必须显式声明分支标签(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>));
不过这个版本并没有优化太多。您似乎是从为调试模式编译的代码中获取的。
下面的版本也是一样的,但是效率更高,主要是不需要标签,只需要一个local:
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>));