如何创建一个比较<>在飞行中

本文关键字:飞行 比较 何创建 创建 一个 | 更新日期: 2023-09-27 18:14:36

我想在代码中通过反射创建一个comparon<>-Delegate。我有这个:

var returnType = typeof (Int32);
var parameters = typeof(Comparison<>).GetMethod("Invoke").GetParameters().Select(x => x.ParameterType).ToArray();
AppDomain domain = AppDomain.CurrentDomain;
AssemblyName aname = new AssemblyName("MyEmissions");
AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule("MainModule", "MyEmissions.dll");
TypeBuilder tb = modBuilder.DefineType("Widget", TypeAttributes.Public);
MethodBuilder mb = tb.DefineMethod("Echo", MethodAttributes.Public | MethodAttributes.Static);
mb.SetSignature(returnType, null, null, parameters, null, null);
ILGenerator gen = mb.GetILGenerator();
gen.Emit(OpCodes.Ldarg_1);
gen.Emit(OpCodes.Ret);
var foo = MulticastDelegate.CreateDelegate(typeof(Comparison<>), mb);

最后一行抛出一个ArgumentException: MethodInfo必须是一个运行时的MethodInfo对象。我是反思和释放的新手,感觉只差了一小步!?

编辑:

我不是一定要创建一个新的程序集-我还尝试了DynamicMethod:

var returnType = typeof (Int32);
var parameters = typeof(Comparison<>).GetMethod("Invoke").GetParameters().Select(x => x.ParameterType).ToArray();
var handler = new DynamicMethod("", returnType, parameters);
var generator = handler.GetILGenerator();
foreach (var parameter in parameters)
{
    var localVariable = generator.DeclareLocal(parameter);
    generator.Emit(OpCodes.Ldloc, localVariable);
}
if (returnType != null)
{
    var returnValue = generator.DeclareLocal(returnType);
    generator.Emit(OpCodes.Ldloc, returnValue);
}
generator.Emit(OpCodes.Ret);
handler.CreateDelegate(typeof(Comparison<>));

抛出BadImageFormatException:/


解决方案:

var returnType = typeof (Int32);
var methodParameters = typeof(Comparison<>).GetMethod("Invoke").GetParameters().Select(x => x.ParameterType.ToString()).ToArray();
AppDomain domain = AppDomain.CurrentDomain;
AssemblyName aname = new AssemblyName("MyEmissions");
AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly(aname, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule("MainModule", "MyEmissions.dll");
TypeBuilder tb = modBuilder.DefineType("Widget", TypeAttributes.Public);
MethodBuilder mb = tb.DefineMethod("Echo", MethodAttributes.Public | MethodAttributes.Static);
GenericTypeParameterBuilder[] typeParameters = mb.DefineGenericParameters(methodParameters);
mb.SetReturnType(returnType);
mb.SetParameters(typeParameters);
ILGenerator gen = mb.GetILGenerator();
gen.Emit(OpCodes.Ldnull);
gen.Emit(OpCodes.Ret);
var dt = tb.CreateType();
var mi = dt.GetMethod("Echo");
var gm = mi.MakeGenericMethod(new[] { typeof(string), typeof(string) });
var parameter = MulticastDelegate.CreateDelegate(typeof(Comparison<string>), gm);

如何创建一个比较<>在飞行中

其他人已经指出了您代码中的一些错误。但还有另一个更大的问题:你试图创建泛型方法和解绑定泛型委托。

DynamicMethod的情况下,您根本不能创建泛型方法。在动态组装的情况下,这是可能的,但您必须使用DefineGenericParameters()

如果您设法以某种方式创建泛型方法,则无法创建未绑定的泛型委托。也就是说,你不能创建类型为Comparison<T>的委托,就像你试图做的那样。你必须用某种特定的类型来代替T。例如,您可以创建Comparison<int>

而且,我发现摆弄CIL相当困难,特别是如果你没有太多的经验。通过创建Expression并编译它,创建委托可能会容易得多。

至于你的第二个例子。

你所做的是正确的。但是你发出的代码是不正确的。这就是为什么你得到BadImageFormatException

当前,你的堆栈中还有剩余的值:

generator.Emit(OpCodes.Ldloc, localVariable);

要补救,要么不加载它们,要么再次从堆栈中弹出它们。