C#发出创建动态属性ToString方法

时间:2019-03-21 02:17:40

标签: c# emit

我有一个类似于以下代码的类:

public class MyClass
{
    public int MyProperty1 { get; set; }
}

我希望创建一个动态的方法tostring

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:公共语言运行库检测到无效程序

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


测试链接:C# Emit to create dynamic Property ToString method | C# Online Compiler | .NET Fiddle

1 个答案:

答案 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>));