.NET 反射发出 - 我在 MSIL 中编写此方法时做错了什么?

问题描述 投票:0回答:1

我正在尝试使用 C# 中的反射发出来动态创建一个委托,该委托返回对象上定义的所有属性值的列表。

我从这里给出的例子开始: https://learn.microsoft.com/previous-versions/dotnet/netframework-4.0/exczf7b9(v=vs.100)

然后,我使用 LinqPad 查看我想要发出的代码的 IL。 例如,对于此代码:

void Main()
{
    new Zap{ Name = "1", Value = 2 }.Test();
}


public class Zap
{
    public string Name { get; set; }
    public int Value { get; set; }
    
    public IEnumerable<object> Test()
    {
        return new List<object> {Name, Value}; // This is what I want to do...
    }
}

我得到了

Test()
方法的 IL 代码:

但是当我运行这段代码时,我得到一个

System.InvalidProgramException: 'Common Language Runtime detected an invalid program.'
异常:

new Target().DoMagic();

public class Target
{
    delegate List<object> ListProps();
    public string MyProperty1 { get; set; } = "zap";
    public int MyProperty2 { get; set; } = 77;

    public void DoMagic()
    {
        Type[] methodArgs = { };
        DynamicMethod listPropsMethod = new DynamicMethod("ListProps", typeof(List<object>), methodArgs, typeof(Target).Module);

        ConstructorInfo? constructor = typeof(List<object>).GetConstructor(new Type[0]);
        MethodInfo? addMethod = typeof(List<object>).GetMethod("Add");

        ILGenerator il = listPropsMethod.GetILGenerator();
        il.Emit(OpCodes.Newobj, constructor);

        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, this.GetType().GetProperty(nameof(MyProperty1), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
        il.Emit(OpCodes.Callvirt, addMethod);

        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, this.GetType().GetProperty(nameof(MyProperty2), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
        il.Emit(OpCodes.Box, typeof(int));
        il.Emit(OpCodes.Callvirt, addMethod);

        il.Emit(OpCodes.Ret);

        var listProps = (ListProps)listPropsMethod.CreateDelegate(typeof(ListProps));
        var value = listProps();
        Console.WriteLine("Result : " + JsonSerializer.Serialize(value));
    }
}

我可能错过了很多细节(例如,我不知道调用方法如何知道要使用哪些实例......),但如果有人可以阐明我如何完成这项工作,我将不胜感激。

此外,调用使用

emit
创建的生成委托是否与编写第一个代码片段并编译它一样快?我对用于获取方法/属性信息的反射代码感到困扰,我想知道这是否会导致一些性能问题。

c# .net-core cil reflection.emit
1个回答
2
投票

认为这应该有效:

delegate List<object> ListProps(Target obj);

和:

Type[] methodArgs = { typeof(Target) };
DynamicMethod listPropsMethod = new DynamicMethod("ListProps", typeof(List<object>), methodArgs, typeof(Target).Module);

ConstructorInfo? constructor = typeof(List<object>).GetConstructor(new Type[0]);
MethodInfo? addMethod = typeof(List<object>).GetMethod("Add");

ILGenerator il = listPropsMethod.GetILGenerator();
il.Emit(OpCodes.Newobj, constructor);

il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, this.GetType().GetProperty(nameof(MyProperty1), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
il.Emit(OpCodes.Callvirt, addMethod);

il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, this.GetType().GetProperty(nameof(MyProperty2), BindingFlags.Instance | BindingFlags.Public).GetGetMethod());
il.Emit(OpCodes.Box, typeof(int));
il.Emit(OpCodes.Callvirt, addMethod);

il.Emit(OpCodes.Ret);

var listProps = (ListProps)listPropsMethod.CreateDelegate(typeof(ListProps));
Console.WriteLine("Result : {0}", string.Join(", ", listProps(this)));

变化:

  • 需要传入一个目标
    ldarg.0
  • 需要用
    box
  • 指定目标类型
  • 需要拆分
    ldarg.0
    call
    /
    callvirt
  • 总是更喜欢
    callvirt
    实例方法,除非你真的知道为什么
© www.soinside.com 2019 - 2024. All rights reserved.