Передача и интерпретация параметров — динамические обработчики

У меня относительно динамический процесс обработки событий, и мне нужно иметь возможность интерпретировать параметры, переданные динамическому обработчику, но у меня возникают проблемы с этим.

Обратите внимание, что приведенный ниже код на 100% функционален как есть. Его просто нужно настроить в соответствии с требованиями.

Ниже приведен простой класс, который определяет действие и событие. Метод OnLeftClickEvent() получает объект [] аргументов, который из-за ограничений события должен быть инкапсулирован в EventArgs.

public class SomeSubscriber : SubscriberBase
{
    private Logger<SomeSubscriber> debug = new Logger<SomeSubscriber>();

    public Action LeftClickAction;
    public event EventHandler LeftClickEvent;

    public SomeSubscriber()
    {
        LeftClickAction += OnLeftClickEvent;
    }

    public void OnLeftClickEvent(params object[] args)
    {
        AppArgs eventArgs = new AppArgs(this, args);

        if(LeftClickEvent != null) LeftClickEvent(this, eventArgs);
    }
} 

На принимающей стороне находится класс, который реализует динамический обработчик и запускает событие:

public class EventControllBase : _MonoControllerBase
{
    private Logger<EventControllBase> debug = new Logger<EventControllBase>();

    SomeSubscriber subscriber;

    private void Start()
    {
        subscriber = new SomeSubscriber();

        subscriber.AddHandler("LeftClickEvent", e =>
        {
            debug.LogWarning( string.Format("Received {0} from {1} ", e[1], e[0]) );
            return true;
        });
    }

    private void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {   // Trigger events.
            subscriber.InvokeDelegate("LeftClickAction", (object) new object[]{ this, Input.mousePosition });
        }
    }
}

В методе Start() я определяю динамический обработчик, а в Update() он запускается и передаются нужные данные.

e[1] явно имеет тип EventArgs (в частности, AppArgs:EventArgs), но я не уверен, как получить доступ к членам для получения данных в экземпляре. Я пробовал кастинг, но ничего не вышло.

Вот тело AppArgs, если это поможет:

public class AppArgs : EventArgs
{
public object sender {get; private set;}
private object[] _args;

public AppArgs(object sender, object[] args)
{
    this.sender = sender;
    this._args = args;
}

public object[] args()
{
    return this._args;
}
}

Динамический обработчик

public static class DynamicHandler
{
        /// <summary>
        /// Invokes a static delegate using supplied parameters.
        /// </summary>
        /// <param name="targetType">The type where the delegate belongs to.</param>
        /// <param name="delegateName">The field name of the delegate.</param>
        /// <param name="parameters">The parameters used to invoke the delegate.</param>
        /// <returns>The return value of the invocation.</returns>
        public static object InvokeDelegate(this Type targetType, string delegateName, params object[] parameters)
        {
            return ((Delegate)targetType.GetField(delegateName).GetValue(null)).DynamicInvoke(parameters);
        }

        /// <summary>
        /// Invokes an instance delegate using supplied parameters.
        /// </summary>
        /// <param name="target">The object where the delegate belongs to.</param>
        /// <param name="delegateName">The field name of the delegate.</param>
        /// <param name="parameters">The parameters used to invoke the delegate.</param>
        /// <returns>The return value of the invocation.</returns>
        public static object InvokeDelegate(this object target, string delegateName, params object[] parameters)
        {
            return ((Delegate)target.GetType().GetField(delegateName).GetValue(target)).DynamicInvoke(parameters);
        }

        /// <summary>
        /// Adds a dynamic handler for a static delegate.
        /// </summary>
        /// <param name="targetType">The type where the delegate belongs to.</param>
        /// <param name="fieldName">The field name of the delegate.</param>
        /// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AddHandler(this Type targetType, string fieldName,
            Func<object[], object> func)
        {
            return InternalAddHandler(targetType, fieldName, func, null, false);
        }

        /// <summary>
        /// Adds a dynamic handler for an instance delegate.
        /// </summary>
        /// <param name="target">The object where the delegate belongs to.</param>
        /// <param name="fieldName">The field name of the delegate.</param>
        /// <param name="func">The function which will be invoked whenever the delegate is invoked.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AddHandler(this object target, string fieldName,
            Func<object[], object> func)
        {
            return InternalAddHandler(target.GetType(), fieldName, func, target, false);
        }

        /// <summary>
        /// Assigns a dynamic handler for a static delegate or event.
        /// </summary>
        /// <param name="targetType">The type where the delegate or event belongs to.</param>
        /// <param name="fieldName">The field name of the delegate or event.</param>
        /// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AssignHandler(this Type targetType, string fieldName,
            Func<object[], object> func)
        {
            return InternalAddHandler(targetType, fieldName, func, null, true);
        }

        /// <summary>
        /// Assigns a dynamic handler for a static delegate or event.
        /// </summary>
        /// <param name="target">The object where the delegate or event belongs to.</param>
        /// <param name="fieldName">The field name of the delegate or event.</param>
        /// <param name="func">The function which will be invoked whenever the delegate or event is fired.</param>
        /// <returns>The return value of the invocation.</returns>
        public static Type AssignHandler(this object target, string fieldName, Func<object[], object> func)
        {
            return InternalAddHandler(target.GetType(), fieldName, func, target, true);
        }

        private static Type InternalAddHandler(Type targetType, string fieldName,
            Func<object[], object> func, object target, bool assignHandler)
        {
            Type delegateType;
            var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic |
                               (target == null ? BindingFlags.Static : BindingFlags.Instance);
            var eventInfo = targetType.GetEvent(fieldName, bindingFlags);
            if (eventInfo != null && assignHandler)
                throw new ArgumentException("Event can be assigned.  Use AddHandler() overloads instead.");

            if (eventInfo != null)
            {
                delegateType = eventInfo.EventHandlerType;
                var dynamicHandler = BuildDynamicHandler(delegateType, func);
                eventInfo.GetAddMethod(true).Invoke(target, new Object[] { dynamicHandler });
            }
            else
            {
                var fieldInfo = targetType.GetField(fieldName);
                                                    //,target == null ? BindingFlags.Static : BindingFlags.Instance);
                delegateType = fieldInfo.FieldType;
                var dynamicHandler = BuildDynamicHandler(delegateType, func);
                var field = assignHandler ? null : target == null
                                ? (Delegate)fieldInfo.GetValue(null)
                                : (Delegate)fieldInfo.GetValue(target);
                field = field == null
                            ? dynamicHandler
                            : Delegate.Combine(field, dynamicHandler);
                if (target != null)
                    target.GetType().GetField(fieldName).SetValue(target, field);
                else
                    targetType.GetField(fieldName).SetValue(null, field);
                    //(target ?? targetType).SetFieldValue(fieldName, field);
            }
            return delegateType;
        }

        /// <summary>
        /// Dynamically generates code for a method whose can be used to handle a delegate of type 
        /// <paramref name="delegateType"/>.  The generated method will forward the call to the
        /// supplied <paramref name="func"/>.
        /// </summary>
        /// <param name="delegateType">The delegate type whose dynamic handler is to be built.</param>
        /// <param name="func">The function which will be forwarded the call whenever the generated
        /// handler is invoked.</param>
        /// <returns></returns>
        public static Delegate BuildDynamicHandler(this Type delegateType, Func<object[], object> func)
        {
            var invokeMethod = delegateType.GetMethod("Invoke");
            var parameters = invokeMethod.GetParameters().Select(parm =>
                Expression.Parameter(parm.ParameterType, parm.Name)).ToArray();
            var instance = func.Target == null ? null : Expression.Constant(func.Target);
            var convertedParameters = parameters.Select(parm => Expression.Convert(parm, typeof(object))).Cast<Expression>().ToArray();
            var call = Expression.Call(instance, func.Method, Expression.NewArrayInit(typeof(object), convertedParameters));
            var body = invokeMethod.ReturnType == typeof(void)
                ? (Expression)call
                : Expression.Convert(call, invokeMethod.ReturnType);
            var expr = Expression.Lambda(delegateType, body, parameters);
            return expr.Compile();
        }
    }

person Nestor Ledon    schedule 22.06.2013    source источник
comment
В вашем классе Subscriber нет метода AddHandler, поэтому в данный момент это не сработает. Вы могли бы использовать subscriber.LeftClickEvent += ... - но, честно говоря, непонятно, зачем вам вообще класс Subscriber. Что это добавляет к тому, что .NET уже предоставляет делегатам и событиям?   -  person Jon Skeet    schedule 22.06.2013
comment
@Jon Вероятно, это унаследовано от SubscriberBase. @Xerosigma Можете ли вы включить реализацию AddHandler?   -  person Mike Precup    schedule 22.06.2013
comment
@MikePrecup: Ах, пропустил это, да. Я до сих пор не совсем понимаю, какова цель этого... похоже, что он заново изобретает колесо.   -  person Jon Skeet    schedule 22.06.2013
comment
@MikePrecup Я добавил реализацию AddHandler в самый низ, извините за неправильное направление. И спасибо за ваше время.   -  person Nestor Ledon    schedule 22.06.2013
comment
@JonSkeet Чтобы объяснить шаблон, класс My Subscriber сможет захватывать аргументы в методе OnLeftClickEvent() до того, как обработчик будет запущен. Таким образом, я могу сначала запустить специфическую для объекта логику, а затем получить результат/результат, который мне нужен, обратно в вызывающую сторону. Спасибо за ваш вклад.   -  person Nestor Ledon    schedule 22.06.2013
comment
Я отредактировал ваш заголовок. См. Должны ли вопросы включать «теги» в свои заголовки?, если нет единого мнения, не следует.   -  person John Saunders    schedule 23.06.2013
comment
@JohnSaunders, спасибо! Также хорошо читается.   -  person Nestor Ledon    schedule 23.06.2013


Ответы (1)


Итак, мне удалось решить мою проблему, ниже приведены все изменения, которые я внес в приведенный выше код:

Во-первых, я внес небольшие изменения в AppArgs, которые говорят сами за себя:

public class AppArgs : EventArgs
{
    public object sender {get; private set;}
    public object[] args {get; private set;}

    public AppArgs(object sender, object[] args)
    {
        this.sender = sender;
        this.args = args;
    }
}

Следующим шагом было выяснить, как правильно преобразовать мой объект EventArgs [] обратно в AppArgs:

В EventControllBase.Start()

    private void Start()
    {
        subscriber = new SomeSubscriber();

        subscriber.AddHandler("LeftClickEvent", e =>
        {   
            debug.LogWarning( string.Format("Received {0} from {1} ", ( (AppArgs)e[1] ).args[1], e[0]) );
            return true;
        });
    }

Чтобы уточнить, мне просто нужно было привести e[1] надлежащим образом, например: ( (AppArgs)e[1] )

Теперь я могу свободно обращаться к членам AppArgs так, как мне нужно. Спасибо всем за вашу помощь, это очень ценится.

person Nestor Ledon    schedule 22.06.2013