Как создать Сравнение <> на лету? - PullRequest
1 голос
/ 12 ноября 2011

Я хочу создать Сравнение <> - Делегировать с помощью отражения в коде. У меня есть это:

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 времени выполнения. Я новичок в рефлексии и излучении, и у меня такое ощущение, что пропущен только маленький шаг!?

Edit:

Я не обязан создавать новую сборку - я также попробовал 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 ]

2 голосов
/ 12 ноября 2011

Другие уже указали на некоторые ошибки в вашем коде.Но есть еще одна большая проблема: вы пытаетесь создать универсальный метод и несвязанный универсальный делегат.

В случае DynamicMethod, вы не можете создать универсальный метод вообще .В случае динамической сборки это возможно, но вы должны использовать DefineGenericParameters().

Если вам удалось как-то создать универсальный метод, вы не можете создать несвязанный универсальныйделегировать.То есть вы не можете создать делегат типа Comparison<T>, как вы пытаетесь это сделать.Вы должны заменить определенный тип на T.Так, например, вы могли бы создать Comparison<int>.

Кроме того, я нахожу, что с CIL сложно работать, особенно если у вас мало опыта с ним.Возможно, будет намного проще создать свой делегат, создав Expression и скомпилировав его.

0 голосов
/ 12 ноября 2011

Что касается вашего второго примера.

То, что вы делаете, правильно. НО код, который вы испускаете, неверен. Вот почему вы получаете BadImageFormatException.

В настоящее время в стеке остались значения, оставленные:

generator.Emit(OpCodes.Ldloc, localVariable);

Чтобы исправить, либо не загружайте их, либо снова вытаскивайте их из стека.

...