如何创建比较<>在飞?

时间:2011-11-12 10:18:13

标签: c# reflection comparison reflection.emit

我想通过代码中的反射创建一个比较<> -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);

2 个答案:

答案 0 :(得分:2)

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

如果是DynamicMethodyou can't create generic method at all。在动态装配的情况下,可以使用DefineGenericParameters()

如果您设法以某种方式创建通用方法,则无法创建未绑定的通用委托。也就是说,您无法像尝试那样创建类型为Comparison<T>的委托。您必须将某些特定类型替换为T。因此,例如,您可以创建Comparison<int>

另外,我发现CIL非常困难,特别是如果你没有太多的经验。通过创建Expression并编译它来创建代理可能要容易得多。

答案 1 :(得分:0)

至于你的第二个例子。

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

的原因

目前,您在堆栈上留下的值仍为:

generator.Emit(OpCodes.Ldloc, localVariable);

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