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