取消事件 - msdn c# event




C#動態事件訂閱 (6)

你的意思是:

//reflect out the method to fire as a delegate
EventHandler eventDelegate = 
   ( EventHandler ) Delegate.CreateDelegate(
       typeof( EventHandler ),    //type of event delegate
       objectWithEventSubscriber, //instance of the object with the matching method
       eventSubscriberMethodName, //the name of the method
       true );

這不會進行訂閱,但會給要調用的方法。

編輯:

在這個答案之後澄清了帖子,如果你不知道這個類型,我的例子將無濟於事。

但是.Net中的所有事件都應遵循默認事件模式,因此只要您遵循它,這將與基本的EventHandler一起使用。

您將如何動態訂閱C#事件,以便在給定Object實例和包含事件名稱的String名稱時,您訂閱該事件並在觸發該事件時執行某些操作(例如寫入控制台)?

這似乎是使用反射,這是不可能的,我想避免必要時使用Reflection.Emit,因為目前(對我來說)似乎是這樣做的唯一方法。

/編輯:我不知道事件所需的代表簽名,這是問題的核心

/編輯2:雖然代表性逆轉似乎是一個好的計劃,但我無法做出使用此解決方案所必需的假設



您可以編譯表達式樹以使用不帶任何參數的void方法作為任何類型事件的事件處理程序。 要容納其他事件處理程序類型,您必須以某種方式將事件處理程序的參數映射到事件。

 using System;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Reflection;

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

 class EventRaiser
 { 
     public event EventHandler SomethingHappened;
     public event EventHandler<ExampleEventArgs> SomethingHappenedWithArg;

     public void RaiseEvents()
     {
         if (SomethingHappened!=null) SomethingHappened(this, EventArgs.Empty);

         if (SomethingHappenedWithArg!=null) 
         {
            SomethingHappenedWithArg(this, new ExampleEventArgs{IntArg = 5});
         }
     }
 }

 class Handler
 { 
     public void HandleEvent() { Console.WriteLine("Handler.HandleEvent() called.");}
     public void HandleEventWithArg(int arg) { Console.WriteLine("Arg: {0}",arg);    }
 }

 static class EventProxy
 { 
     //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);
     }

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


 static class Test
 {
     public static void Main()
     { 
        var raiser  = new EventRaiser();
        var handler = new Handler();

        //void delegate with no parameters
        string eventName = "SomethingHappened";
        var eventinfo = raiser.GetType().GetEvent(eventName);
        eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,handler.HandleEvent));

        //void delegate with one parameter
        string eventName2 = "SomethingHappenedWithArg";
        var eventInfo2 = raiser.GetType().GetEvent(eventName2);
        eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,handler.HandleEventWithArg));

        //or even just:
        eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,()=>Console.WriteLine("!")));  
        eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,i=>Console.WriteLine(i+"!")));

        raiser.RaiseEvents();
     }
 }

我最近寫了一系列描述單元測試事件的博客文章,我討論的一種技術描述了動態事件訂閱。 我使用了反射和MSIL(代碼發射)來實現動態方面,但這一切都很好。 使用DynamicEvent類,可以動態訂閱事件,如下所示:

EventPublisher publisher = new EventPublisher();

foreach (EventInfo eventInfo in publisher.GetType().GetEvents())
{
    DynamicEvent.Subscribe(eventInfo, publisher, (sender, e, eventName) =>
    {
        Console.WriteLine("Event raised: " + eventName);
    });
}

我實現的模式的一個特性是它將事件名稱注入到事件處理程序的調用中,因此您知道已經引發了哪個事件。 對單元測試非常有用。

博客文章非常冗長,因為它描述了事件單元測試技術,但提供了完整的源代碼和測試,並在最後一篇文章中詳細描述瞭如何實現動態事件訂閱。

http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/


試試LinFu - 它有一個通用的事件處理程序,可以讓你在運行時綁定到任何事件。 例如,您可以將處理程序綁定到動態按鈕的Click事件:

// Note: The CustomDelegate signature is defined as:
// public delegate object CustomDelegate(params object[] args);
CustomDelegate handler = delegate
                         {
                           Console.WriteLine("Button Clicked!");
                           return null;
                         };

Button myButton = new Button();
// Connect the handler to the event
EventBinder.BindToEvent("Click", myButton, handler);

LinFu允許您將處理程序綁定到任何事件,而不管委託簽名如何。 請享用!

你可以在這裡找到它: http://www.codeproject.com/KB/cs/LinFuPart3.aspxhttp://www.codeproject.com/KB/cs/LinFuPart3.aspx


這不是一個完全通用的解決方案,但是如果你的所有事件都是void Foo(object o,T args),其中T派生自EventArgs,那麼你可以使用委託逆變來逃避它。 像這樣(KeyDown的簽名與Click的簽名不同):

    public Form1()
    {
        Button b = new Button();
        TextBox tb = new TextBox();

        this.Controls.Add(b);
        this.Controls.Add(tb);
        WireUp(b, "Click", "Clickbutton");
        WireUp(tb, "KeyDown", "Clickbutton");
    }

    void WireUp(object o, string eventname, string methodname)
    {
        EventInfo ei = o.GetType().GetEvent(eventname);

        MethodInfo mi = this.GetType().GetMethod(methodname, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);

        Delegate del = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);

        ei.AddEventHandler(o, del);

    }
    void Clickbutton(object sender, System.EventArgs e)
    {
        MessageBox.Show("hello!");
    }




delegates