我正在尝试编写一个表达式树,它可以使用EventInfo
给出的方法订阅MethodInfo
给出的事件。表达式树应该编译成Action<object, object>
,其中参数是事件源对象和订阅对象。 EventInfo和MethodInfos保证兼容。
这是我到目前为止所做的:
// Given the following
object Source = /**/; // the object that will fire an event
EventInfo SourceEvent = /**/; // the event that will be fired
object Target = /**/; // the object that will subscribe to the event
MethodInfo TargetMethod = /**/; // the method that will react to the event
// 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);
// Get subscribing method group. This is where things fail
var targetMethodRef = Expression.MakeMemberAccess(targetParamCast, TargetMethod);
// Subscribe to the event
var addMethodCall = Expression.Call(sourceParamCast, SourceEvent.AddMethod, targetMethodRef);
var lambda = Expression.Lambda<Action<object, object>>(addMethodCall, sourceParam, targetParam);
var subscriptionAction = lambda.Compile();
// And then later, subscribe to the event
subscriptionAction(Source, Target);
在致电MakeMemberAccess
时,我收到以下异常:
ArgumentException:Member&#39; void theMethodName()&#39;不是字段或属性
此处的目标是targetMethodRef
基本上代表在使用方法订阅事件时+=
右侧显示的内容。
TLDR:如何创建表达式,将对象上的方法组作为参数传递给表达式树中的函数调用?
答案 0 :(得分:1)
应该是这样。这里的复杂性是您必须使用CreateDelegate
在lambda方法中创建委托。遗憾的是,似乎没有可能创建一个开放的委托(没有target
的委托)在lambda方法中编译然后&#34;关闭&#34;执行lambda方法时,它在lambda方法中。或者至少我不知道该怎么做。 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
有一个带有两个参数的构造函数,目标object
和IntPtr
,它是该方法的本机函数指针。它通常由CIL使用ldftn
/ ldvirtftn
,但.MethodHandle.GetFunctionPointer()
是相同的&#34;事物&#34;。所以我们在我们构建的lambda表达式中调用这个构造函数。