2013-03-24 40 views
0

我在我的控制器中有以下內容,並希望將float類型的x,y和z傳遞給訂閱者。然而,我遇到了一些困難。我需要調整什麼以允許我通過我的浮點​​數(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}); 
     } 
    } 
} 

處理程序:

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

,我也加入了一些代碼來測試方法,我可以只使用簡單的lambda表達式時,我指定回調的代表,但我寧願使用「返回true」定義,因爲我設置斷點,以檢查函數實際上被調用。

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

非常感謝你的好先生!我還沒有找到解決方案,我會盡快嘗試您的代碼。 – 2013-03-29 15:38:02

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 + "!"))); 

設計方面這種解決方案是不是最好的,它使用當前d設計你已經展示了,但是我對使用這種方法維護可擴展和易於遵循的體系結構有強烈的保留意見。 我建議你創建一個適配器/轉換器,它將從EventArgs類生成lambda參數,通過實現一個接口可以很容易地在EventArgs中指定它們,這樣getArgExpression將只使用轉換器來生成適當的參數,並且你可以擺脫很多if (Type == typeof(...))

這將需要我多一點的時間來制定解決方案,但如果您真的感興趣,我可以嘗試寫下來併發布。

+0

非常感謝您的強烈反應。我目前正在盡最大努力改進我的活動實施,如果能向我展示更好的解決方案,我將不勝感激。謝謝。 – 2013-03-25 21:15:47

+0

在網上搜索,我看到您的代碼段是從http://stackoverflow.com/questions/45779/c-sharp-dynamic-event-subscription採取這種實現需要,你可以通過使用事件名稱註冊的事件處理程序。如果你能給我更多關於你想要達到什麼的細節,我可以想出一個更適合你的需求的解決方案。您是否需要提升和處理MouseEventArgs,或者是否需要針對多個通用場景的解決方案? – mematei 2013-03-25 21:38:56

+0

這確實是片段。我的目標是創建一個通用解決方案,以接受多個案例中類似類型的參數。例如MouseEventArgs和WorldPositionArgs都會帶X,Y,Z浮點數。希望能夠幫助和再次感謝您的幫助。 – 2013-03-25 21:45:47