2013-03-24 4 views
0

У меня есть нижеследующее в моем контроллере и вы хотите передать x, y и z типа float подписчикам. Однако у меня есть некоторые трудности. Что мне нужно настроить, чтобы позволить мне передавать мои поплавки (x, y, z) в качестве параметров? Спасибо.C# Параметры передачи - Подписка на динамические события

private void ProcessEvents() 
{ 
    if(Input.GetMouseButtonDown(1)) 
    { 
     // Data to be passed to subscribers 
     float x = Input.mousePosition.x; 
     float y = Input.mousePosition.y; 
     float z = Input.mousePosition.z; 

     var publisher = new EventPublisher(); 
     var handler = new Handler(); 

     // Void delegate with one parameter 
     string eventName = "RightClickEvent"; 
     var rightClickEvent = publisher.GetType().GetEvent(eventName); 

     rightClickEvent.AddEventHandler(publisher, EventProxy.Create<int>(rightClickEvent, i=>Debug.LogError(i + "!"))); 

     publisher.PublishEvents(); 
    } 
} 

Другие источники:

public class ExampleEventArgs : EventArgs 
{ 
    public int IntArg {get; set;} 
} 

событий Издательство:

public class EventPublisher 
{ 
    public event EventHandler<ExampleEventArgs> RightClickEvent; 

    /// <summary> 
    /// Publishes the events. 
    /// </summary> 
    public void PublishEvents() 
    { 
     if(RightClickEvent != null) 
     { 
      RightClickEvent(this, new ExampleEventArgs{IntArg = 5}); 
     } 
    } 
} 

Handler:

public class Handler 
{ 
    public void HandleEventWithArg(int arg) { Debug.LogError("Arg: " + string.Format("[{0}]", arg)); } 
} 

EventProxy:

static class EventProxy 
{ 
    // Void delegate with one parameter 
    static public Delegate Create<T>(EventInfo evt, Action<T> d) 
    { 
     var handlerType = evt.EventHandlerType; 
     var eventParams = handlerType.GetMethod("Invoke").GetParameters(); 

     //lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg) 
     var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray(); 
     var arg = getArgExpression(parameters[1], typeof(T)); 
     var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg); 
     var lambda = Expression.Lambda(body,parameters); 
     return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); 
    } 

    // Returns an expression that represents an argument to be passed to the delegate 
    static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType) 
    { 
     if(eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int)) 
     { 
      //"x1.IntArg" 
      var memberInfo = eventArgs.Type.GetMember("IntArg")[0]; 
      return Expression.MakeMemberAccess(eventArgs,memberInfo); 
     } 
     throw new NotSupportedException(eventArgs + "->" + handlerArgType); 
    } 

    // Void delegates with no parameters 
    static public Delegate Create(EventInfo evt, Action d) 
    { 
     var handlerType = evt.EventHandlerType; 
     var eventParams = handlerType.GetMethod("Invoke").GetParameters(); 

     //lambda: (object x0, EventArgs x1) => d() 
     var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")); 
     var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke")); 
     var lambda = Expression.Lambda(body,parameters.ToArray()); 
     return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); 
    } 
} 

ответ

1

Итак, я прочитал, что вы сказали о развязке модулей приложений в стиле MVC. Обычно мне нравится работать с сильным типизированным кодом, даже при использовании рефлексии, но я относительно новичок в MVC и не знаю рекомендуемых практик. Вы знаете свои требования лучше, чем я, поэтому я просто редактировал решение Nguyen, потому что я считаю, что он использовал некоторые расширения, которые не были включены в файл, и опубликовал результат здесь. Все кредиты идут Nguyen.

namespace Dynamics 
{ 
    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(); 
     } 
    } 
} 

И я также добавил код для тестирования методов, я мог бы просто использовать простые лямбды, когда я назначил делегат обратного вызова, но я предпочитаю использовать «возвращение истинные» определений, потому что я установить точки останова, чтобы проверить, что функции фактически называются.

class TestClass 
{ 
    private void Test() 
    { 
     TestInstance(); 
     TestStatic(); 
    } 

    private void TestInstance() 
    { 
     var eventClass = new EventClass(); 
     eventClass.InvokeDelegate("InstanceEventRaiseDelegate"); 
     eventClass.AddHandler("InstanceEvent", parameters => 
      { 
       return true; 
      }); 
     eventClass.AddHandler("InstanceEventRaiseDelegate", parameters => 
     { 
      return true; 
     }); 
     eventClass.InvokeDelegate("InstanceEventRaiseDelegate"); 

     eventClass.AssignHandler("InstanceEventRaiseDelegate", parameters => 
     { 
      return true; 
     }); 
     eventClass.InvokeDelegate("InstanceEventRaiseDelegate"); 
    } 

    private void TestStatic() 
    { 
     typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate"); 
     typeof(EventClass).AddHandler("StaticEvent", parameters => 
     { 
      return true; 
     }); 
     typeof(EventClass).AddHandler("StaticEventRaiseDelegate", parameters => 
     { 
      return true; 
     }); 
     typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate"); 
     typeof(EventClass).AssignHandler("StaticEventRaiseDelegate", parameters => 
     { 
      return true; 
     }); 
     typeof(EventClass).InvokeDelegate("StaticEventRaiseDelegate"); 
    } 

    public class EventClass 
    { 

     #region Instance 

     public EventClass() 
     { 
      InstanceEventRaiseDelegate = OnInstanceEvent; 
     } 

     public Action InstanceEventRaiseDelegate; 

     public event EventHandler InstanceEvent; 

     public void OnInstanceEvent() 
     { 
      if (InstanceEvent != null) 
       InstanceEvent(this, EventArgs.Empty); 
     } 

     #endregion 

     #region Static 

     static EventClass() 
     { 
      StaticEventRaiseDelegate = OnStaticEvent; 
     } 

     public static Action StaticEventRaiseDelegate; 

     public static event EventHandler StaticEvent; 

     public static void OnStaticEvent() 
     { 
      if (StaticEvent != null) 
       StaticEvent(null, EventArgs.Empty); 
     } 

     #endregion 
    } 
} 

Извините за поздний ответ, но, похоже, вы смогли найти решение в другом месте :), goodluck.

+0

Спасибо вам большое, сэр! Я еще не нашел решение, я попробую ваш код, как только у меня появится шанс. –

1

Я добавил код, который позволит достичь того, что у требуют:

Я изменил функцию getArgExpression к этому

// Returns a List of expressions that represent the arguments to be passed to the delegate 
static IEnumerable<Expression> getArgExpression(ParameterExpression eventArgs, Type handlerArgType) 
{ 
    if (eventArgs.Type == typeof(ExampleEventArgs) && handlerArgType == typeof(int)) 
    { 
     //"x1.IntArg" 
     var memberInfo = eventArgs.Type.GetMember("IntArg")[0]; 
     return new List<Expression> { Expression.MakeMemberAccess(eventArgs, memberInfo) }; 
    } 
    if (eventArgs.Type == typeof(MousePositionEventArgs) && handlerArgType == typeof(float)) 
    { 
     //"x1.X" 
     var xMemberInfo = eventArgs.Type.GetMember("X")[0]; 
     //"x1.Y" 
     var yMemberInfo = eventArgs.Type.GetMember("Y")[0]; 
     //"x1.Z" 
     var zMemberInfo = eventArgs.Type.GetMember("Z")[0]; 
     return new List<Expression> 
        { 
         Expression.MakeMemberAccess(eventArgs, xMemberInfo), 
         Expression.MakeMemberAccess(eventArgs, yMemberInfo), 
         Expression.MakeMemberAccess(eventArgs, zMemberInfo), 
        }; 
    } 
    throw new NotSupportedException(eventArgs + "->" + handlerArgType); 
} 

Функция вызова остается неизменным, потому что он имеет перегрузку IEnumerable<Expression>. Затем я добавил EventArgs, специфичные для вашей MousePosition ситуации

public class MousePositionEventArgs : EventArgs 
{ 
    public float X { get; set; } 
    public float Y { get; set; } 
    public float Z { get; set; } 
} 

Далее больше я написал новую функцию «EventProxy», который будет обрабатывать делегат с 3 параметрами одного и того же типа

// Void delegate with three parameters 
static public Delegate Create<T>(EventInfo eventInformation, Action<T, T, T> lambdaDelegate) 
{ 
    var handlerType = eventInformation.EventHandlerType; 
    var eventParams = handlerType.GetMethod("Invoke").GetParameters(); 

    //lambda: (object x0, ExampleEventArgs x1) => d(x1.X,x1.Y,x1.Z) 
    var parameters = eventParams.Select(p => Expression.Parameter(p.ParameterType, "x")).ToArray(); 
    var arg = getArgExpression(parameters[1], typeof(T)); 
    var body = Expression.Call(Expression.Constant(lambdaDelegate), lambdaDelegate.GetType().GetMethod("Invoke"), arg); 
    var lambda = Expression.Lambda(body, parameters); 
    return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); 
} 

Теперь мы можно легко добавить подписку MouseEvent, используя следующий код

rightClickEvent.AddEventHandler(publisher, EventProxy.Create<float>(rightClickEvent, (t, u, v) => Console.Write(t + "x" + u + "x" + v + "!"))); 

Продуманный дизайн это решение не самое лучшее, он использует текущую д которые вы показали, но у меня есть серьезные оговорки в отношении сохранения масштабируемой и простой в использовании архитектуры с этим подходом. Я бы предложил вам создать адаптер/конвертер, который будет генерировать параметры лямбда из класса EventArgs, их можно легко указать в EventArgs, реализовав интерфейс, таким образом getArgExpression будет использовать конвертер только для генерации соответствующих параметров и вы можете избавиться от многих if (Type == typeof(...)).

Мне потребуется больше времени, чтобы составить это решение, но если вы действительно заинтересованы, я могу попытаться записать его и опубликовать.

+0

Большое спасибо за ваш надежный отклик. В настоящее время я изо всех сил стараюсь улучшить реализацию своего мероприятия и очень признателен, если бы смог показать мне лучшее решение. Спасибо. –

+0

При поиске в Интернете я вижу, что ваш фрагмент кода был взят из http://stackoverflow.com/questions/45779/c-sharp-dynamic-event-subscription, для этой реализации требуется, чтобы вы могли зарегистрировать обработчик событий, используя имя события , Если бы вы могли дать мне несколько подробностей относительно того, что именно вы пытаетесь достичь, я мог бы найти решение, которое могло бы лучше соответствовать вашим потребностям. Вам нужно поднять и обработать только MouseEventArgs или вам нужно решение для нескольких сценариев общего назначения? – mematei

+0

Это действительно фрагмент. Моя цель - создать общее решение, которое будет принимать параметры аналогичных типов для нескольких случаев. Например, MouseEventArgs и WorldPositionArgs будут принимать поплавки X, Y, Z. Надеюсь, что это поможет и благодарит вас за вашу помощь. –

Смежные вопросы