简体   繁体   English

带 ref / out 参数的动态事件订阅

[英]Dynamic event subscription with ref / out parameter

I have been using an expression tree to create a delegate and subscribe to any event with an Action<Object[]> , where all the event parameters are converted to an array of objects.我一直在使用表达式树来创建委托并使用Action<Object[]>订阅任何事件,其中所有事件参数都转换为对象数组。 This has been working fine until now, that I need to subscribe to an event with an out/ref parameter and I need to set the value of this parameter.到目前为止,这一直运行良好,我需要使用 out/ref 参数订阅事件,并且需要设置此参数的值。

Is there a way to use something similar to the expression tree and subscribe to any event, but still be able to set/return values to the object that raised the event?有没有办法使用类似于表达式树的东西并订阅任何事件,但仍然能够将值设置/返回给引发事件的 object?

Expression tree to create a delegate:创建委托的表达式树:

public static Delegate CreateProxyWithDynamicParameters(this EventInfo EventInfo, Action<object[]> Action)
{
        var EventHandlerType = EventInfo.EventHandlerType;
        var InvokeMethodInfo = EventHandlerType.GetMethod("Invoke");

        var Parameters = InvokeMethodInfo.GetParameters().Select(Parameter => Expression.Parameter(Parameter.ParameterType, Parameter.Name)).ToArray();

        var ConvertedParameters = Parameters.Select(Parameter => Expression.Convert(Parameter, typeof(object))).Cast<Expression>().ToArray();

        var NewArrayInit = Expression.NewArrayInit(typeof(object), ConvertedParameters);

        var Body = Expression.Call(Expression.Constant(Action), "Invoke", null, NewArrayInit);
        var lambdaExpression = Expression.Lambda(Body, Parameters);

        return Delegate.CreateDelegate(EventInfo.EventHandlerType, lambdaExpression.Compile(), InvokeMethodInfo.Name, ignoreCase: false);
}

Subscription with the delegate:与委托人订阅:

protected static void AddEvent<TMessage>(EventInfo eventInfo) where TMessage : EventArgs
{
        void EventAction(object[] e)
        {
            // Run some event code.
        }

        var @delegate = eventInfo.CreateProxyWithDynamicParameters(EventAction);
        DelegateDictionary[typeof(TMessage)] = @delegate;

        eventInfo.AddEventHandler(x.Target, @delegate);
}

Edit:编辑:

Here is a vanilla simple.这是一个简单的香草。 Where I need to set the HandlingCode parameter.我需要设置HandlingCode参数的地方。

InventorApplication.ApplicationEvents.OnSaveDocument += ApplicationEvents_OnSaveDocument;

private void ApplicationEvents_OnSaveDocument(_Document DocumentObject, EventTimingEnum BeforeOrAfter, NameValueMap Context, out HandlingCodeEnum HandlingCode)
{
    HandlingCode = HandlingCodeEnum.kEventHandled;
}

You are going to struggle to do this with Expression , because Expression likes to be able to run things via reflection for fallback, and it can't handle refs via reflection.您将很难使用Expression来做到这一点,因为Expression喜欢能够通过反射运行事物以进行回退,并且它不能通过反射处理引用。 You might be able to do it via raw IL, but... that's getting messier and messier.也许可以通过原始 IL 来做到这一点,但是……这变得越来越混乱。

Frankly, this event API is making life hard for you.坦率地说,这次活动 API 让您的生活变得艰难。 If you control that API, I strongly suggest using the more idiomatic (object sender, SomeEventArgs args) where SomeEventArgs: EventArgs .如果您控制 API,我强烈建议使用更惯用的(object sender, SomeEventArgs args) ,其中SomeEventArgs: EventArgs Your SomeEventArgs would then have properties for all the things you're trying to pass as parameters, and the interested code can just do:然后,您的SomeEventArgs将具有您尝试作为参数传递的所有内容的属性,并且感兴趣的代码可以执行以下操作:

args.HandlingCode = ...

to assign a value.分配一个值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM