Так и должно быть.Сложность здесь заключается в том, что вам нужно создать делегат внутри лямбда-метода с CreateDelegate
.К сожалению, кажется невозможным создать открытый делегат (делегат без target
) для компиляции внутри лямбда-метода и затем «закрыть» его внутри лямбда-метода при выполнении лямбда-метода.Или, по крайней мере, я не знаю, как это сделать.CreateDelegate
к сожалению, это немного медленно.
static Action<object, object> MakeFunc(EventInfo sourceEvent, MethodInfo targetMethod)
{
// setting up objects involved
var sourceParam = Expression.Parameter(typeof(object), "source");
var targetParam = Expression.Parameter(typeof(object), "target");
var sourceParamCast = Expression.Convert(sourceParam, sourceEvent.DeclaringType);
var targetParamCast = Expression.Convert(targetParam, targetMethod.DeclaringType);
var createDelegate = typeof(Delegate).GetMethod(nameof(Delegate.CreateDelegate), BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(Type), typeof(object), typeof(MethodInfo) }, null);
// Create a delegate of type sourceEvent.EventHandlerType
var createDelegateCall = Expression.Call(createDelegate, Expression.Constant(sourceEvent.EventHandlerType), targetParam, Expression.Constant(targetMethod));
// Cast the Delegate to its real type
var delegateCast = Expression.Convert(createDelegateCall, sourceEvent.EventHandlerType);
// Subscribe to the event
var addMethodCall = Expression.Call(sourceParamCast, sourceEvent.AddMethod, delegateCast);
var lambda = Expression.Lambda<Action<object, object>>(addMethodCall, sourceParam, targetParam);
var subscriptionAction = lambda.Compile();
return subscriptionAction;
}
Ммм ... можно сделать, вызвав конструктор делегата.Построен пробным путем (не нашел много документации по этому поводу):
static Action<object, object> MakeFunc(EventInfo sourceEvent, MethodInfo targetMethod)
{
// setting up objects involved
var sourceParam = Expression.Parameter(typeof(object), "source");
var targetParam = Expression.Parameter(typeof(object), "target");
var sourceParamCast = Expression.Convert(sourceParam, sourceEvent.DeclaringType);
var targetParamCast = Expression.Convert(targetParam, targetMethod.DeclaringType);
ConstructorInfo delegateContructror = sourceEvent.EventHandlerType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(object), typeof(IntPtr) }, null);
IntPtr fp = targetMethod.MethodHandle.GetFunctionPointer();
// create the delegate
var newDelegate = Expression.New(delegateContructror, targetParam, Expression.Constant(fp));
// Subscribe to the event
var addMethodCall = Expression.Call(sourceParamCast, sourceEvent.AddMethod, newDelegate);
var lambda = Expression.Lambda<Action<object, object>>(addMethodCall, sourceParam, targetParam);
var subscriptionAction = lambda.Compile();
return subscriptionAction;
}
Delegate
s имеет конструктор с двумя параметрами: target object
и IntPtr
, который является указателем на встроенную функциюк методу.Обычно он используется CIL с ldftn
/ ldvirtftn
, но .MethodHandle.GetFunctionPointer()
- это то же самое.Поэтому мы вызываем этот конструктор внутри лямбда-выражения, которое мы строим.