Краткое резюме
Да, вы можете, но вам придется проделать дополнительную работу. Перейдите к фрагменту кода, чтобы сделать это.
Длинный ответ
Эта проблема
Не напрямую, нет. Как отмечено в SO Question: LambdaExpression CompileToMethod, в то время как LambdaExpression.CompileToMethod
в .NET 4.0 делает возьмите MethodBuilder
, он может представлять только статический метод.
Частичное решение
Итак, вам нужно обойти это ограничение, сначала создав ссылку на статический метод, а затем создав метод экземпляра, который вызывает этот статический метод. Если в вашем выражении нет никаких «живых объектов» (т. е. вы используете существующую ссылку на объект при создании выражения), то довольно просто создать статический метод, а затем создать метод экземпляра, который вызывает статический метод. . Однако, если в вашем выражении есть "живой объект", CompileToMethod
сообщит вам, что не может использовать выражение, потому что в вашем выражении есть живой объект.
Полное решение
Вместо создания статического метода вы можете добавить поле делегата к сгенерированному типу, а затем из метода экземпляра вызвать поле делегата и передать аргументы метода делегату.
Code
Предположим, что TypeBuilder
называется _typeBuilder
, MethodBuilder
называется methodBuilder
, а делегат перенаправляется на имя delegateToInvoke
:
// create a field to hold the dynamic delegate
var fieldBuilder = _typeBuilder.DefineField(
"<>delegate_field",
delegateToInvoke.GetType(),
FieldAttributes.Private);
// remember to set it later when we create a new instance
_fieldsToSet.Add(new KeyValuePair<FieldInfo, object>(fieldBuilder, delegateToInvoke));
var il = methodBuilder.GetILGenerator();
// push the delegate onto the stack
il.Emit(OpCodes.Ldarg_0);
// by loading the field
il.Emit(OpCodes.Ldfld, fieldBuilder);
// if the delegate has a target, that means the first argument is really a pointer to a "this"
// object/closure, and we don't want to forward it. Thus, we skip it and continue as if it
// wasn't there.
if (delegateToInvoke.Target != null)
{
parameters = parameters.Skip(1).ToArray();
}
// push each argument onto the stack (thus "forwarding" the arguments to the delegate).
for (int i = 0; i < parameters.Length; i++)
{
il.Emit(OpCodes.Ldarg, i + 1);
}
// call the delegate and return
il.Emit(OpCodes.Callvirt, delegateToInvoke.GetType().GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
Когда вы создаете новый экземпляр, обязательно установите поле перед использованием экземпляра:
generatedType.GetField("<>delegate_field", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(instance, delegateToInvoke);
person
zastrowm
schedule
01.02.2014