我正在尝试使用 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
创建的生成委托是否与编写第一个代码片段并编译它一样快?我对用于获取方法/属性信息的反射代码感到困扰,我想知道这是否会导致一些性能问题。
我认为这应该有效:
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
实例方法,除非你真的知道为什么