首页 文章

了解C#中的事件和事件处理程序

提问于
浏览
278

我理解事件的目的,特别是在创建用户界面的环境中 . 我认为这是创建活动的原型:

public void EventName(object sender, EventArgs e);

事件处理程序做了什么,为什么需要它们,以及如何创建一个?

9 回答

  • 92

    出版商:事件发生的地方 . Publisher应指定该类正在使用哪个委托并生成必要的参数,将这些参数及其自身传递给委托 .

    订户:响应发生的地方 . 订阅者应指定响应事件的方法 . 这些方法应采用与委托相同类型的参数 . 订阅者然后将此方法添加到发布者的委托 .

    因此,当事件发生在发布者中时,委托会收到一些事件参数(数据等),但发布者不知道所有这些数据会发生什么 . 订阅者可以在自己的类中创建方法来响应发布者类中的事件,以便订阅者可以响应发布者的事件 .

  • 20

    C#知道两个术语, delegateevent . 让我们从第一个开始 .

    代表

    delegate 是对方法的引用 . 就像您可以创建对实例的引用一样:

    MyClass instance = myFactory.GetInstance();
    

    您可以使用委托创建对方法的引用:

    Action myMethod = myFactory.GetInstance;
    

    现在您已经有了对方法的引用,您可以通过引用调用该方法:

    MyClass instance = myMethod();
    

    但你为什么要这样?您也可以直接拨打 myFactory.GetInstance() . 在这种情况下你可以 . 但是,有很多情况需要考虑您不希望应用程序的其余部分知道 myFactory 或直接调用 myFactory.GetInstance() .

    一个显而易见的是,如果你想能够从一个中心位置(又名 factory method pattern )将 myFactory.GetInstance() 替换为 myOfflineFakeFactory.GetInstance() .

    工厂方法模式

    所以,如果你有一个 TheOtherClass 类并且它需要使用 myFactory.GetInstance() ,这就是代码在没有代理的情况下的样子(你需要让 TheOtherClass 知道 myFactory 的类型):

    TheOtherClass toc;
    //...
    toc.SetFactory(myFactory);
    
    
    class TheOtherClass
    {
       public void SetFactory(MyFactory factory)
       {
          // set here
       }
    
    }
    

    如果您使用委托,则不必公开我的工厂类型:

    TheOtherClass toc;
    //...
    Action factoryMethod = myFactory.GetInstance;
    toc.SetFactoryMethod(factoryMethod);
    
    
    class TheOtherClass
    {
       public void SetFactoryMethod(Action factoryMethod)
       {
          // set here
       }
    
    }
    

    因此,您可以将委托给一些其他类使用,而不会将您的类型暴露给它们 . 你唯一暴露的是方法的签名(你有多少参数等等) .

    “我的方法的签名”,我之前在哪里听到过?是的,接口!!!接口描述了整个类的签名 . 将代表视为仅描述一种方法的签名!

    接口和委托之间的另一个巨大区别是,当您编写类时,您不必对C#说“此方法实现了该类型的委托” . 使用接口,您需要说“此类实现了该类型的接口” .

    此外,委托引用可以(有一些限制,见下文)引用多个方法(称为 MulticastDelegate ) . 这意味着当您调用委托时,将执行多个显式附加的方法 . 对象引用始终只能引用一个对象 .

    MulticastDelegate 的限制是(方法/委托)签名不应具有任何返回值( void ),并且签名中不使用关键字 outref . 显然,你不能调用两个返回数字的方法,并期望它们返回相同的数字 . 签名符合后,委托自动为 MulticastDelegate .

    事件

    事件只是属性(如get; set;属性到实例字段),它们从其他对象公开对委托的订阅 . 但是这些属性不支持get; set; . 相反,他们支持添加;删除;

    所以你可以:

    Action myField;
    
        public event Action MyProperty
        {
            add { myField += value; }
            remove { myField -= value; }
        }
    

    用户界面(WinForms)

    所以,现在我们知道一个委托是一个方法的引用,我们可以有一个事件让世界知道他们可以给我们他们的方法从我们的委托中引用,我们是一个UI按钮,然后:我们可以问任何对我是否被点击感兴趣的人,向我们注册他们的方法(通过我们公开的事件) . 我们可以使用提供给我们的所有方法,并由我们的代表引用它们 . 然后,我们等待等待......直到用户点击该按钮,然后我们才有足够的理由调用该委托 . 并且因为委托引用了给我们的所有方法,所以这些方法都将被调用 . 我们不知道那些方法做了什么,也不知道哪个类实现了这些方法 . 我们所关心的是有人对我们被点击感兴趣,并且给了我们一个符合我们所需签名的方法的参考 .

    Java

    像Java这样的语言没有委托 . 他们使用接口代替 . 他们这样做的方式是问任何对“我们被点击”感兴趣的人,实现某个界面(我们可以调用某种方法),然后给我们实现界面的整个实例 . 我们保留了实现此接口的所有对象的列表,并且可以调用它们的“特定方法”每当我们点击时都会打电话 .

  • 32
    //This delegate can be used to point to methods
    //which return void and take a string.
    public delegate void MyDelegate(string foo);
    
    //This event can cause any method which conforms
    //to MyEventHandler to be called.
    public event MyDelegate MyEvent;
    
    //Here is some code I want to be executed
    //when SomethingHappened fires.
    void MyEventHandler(string foo)
    {
        //Do some stuff
    }
    
    //I am creating a delegate (pointer) to HandleSomethingHappened
    //and adding it to SomethingHappened's list of "Event Handlers".
    myObj.MyEvent += new MyDelegate (MyEventHandler);
    
  • 0

    这是一个可能有用的代码示例:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace Event_Example
    {
      // First we have to define a delegate that acts as a signature for the
      // function that is ultimately called when the event is triggered.
      // You will notice that the second parameter is of MyEventArgs type.
      // This object will contain information about the triggered event.
    
      public delegate void MyEventHandler(object source, MyEventArgs e);
    
      // This is a class which describes the event to the class that receives it.
      // An EventArgs class must always derive from System.EventArgs.
    
      public class MyEventArgs : EventArgs
      {
        private string EventInfo;
    
        public MyEventArgs(string Text) {
          EventInfo = Text;
        }
    
        public string GetInfo() {
          return EventInfo;
        }
      }
    
      // This next class is the one which contains an event and triggers it
      // once an action is performed. For example, lets trigger this event
      // once a variable is incremented over a particular value. Notice the
      // event uses the MyEventHandler delegate to create a signature
      // for the called function.
    
      public class MyClass
      {
        public event MyEventHandler OnMaximum;
    
        private int i;
        private int Maximum = 10;
    
        public int MyValue
        {
          get { return i; }
          set
          {
            if(value <= Maximum) {
              i = value;
            }
            else 
            {
              // To make sure we only trigger the event if a handler is present
              // we check the event to make sure it's not null.
              if(OnMaximum != null) {
                OnMaximum(this, new MyEventArgs("You've entered " +
                  value.ToString() +
                  ", but the maximum is " +
                  Maximum.ToString()));
              }
            }
          }
        }
      }
    
      class Program
      {
        // This is the actual method that will be assigned to the event handler
        // within the above class. This is where we perform an action once the
        // event has been triggered.
    
        static void MaximumReached(object source, MyEventArgs e) {
          Console.WriteLine(e.GetInfo());
        }
    
        static void Main(string[] args) {
          // Now lets test the event contained in the above class.
          MyClass MyObject = new MyClass();
          MyObject.OnMaximum += new MyEventHandler(MaximumReached);
          for(int x = 0; x <= 15; x++) {
            MyObject.MyValue = x;
          }
          Console.ReadLine();
        }
      }
    }
    
  • 564

    要了解事件处理程序,您需要了解delegates . 在C#中,您可以将委托视为方法的指针(或引用) . 这很有用,因为指针可以作为值传递 .

    代表的核心概念是其签名或形状 . 那是(1)返回类型和(2)输入参数 . 例如,如果我们创建一个委托 void MyDelegate(object sender, EventArgs e) ,它只能指向返回 void 的方法,并取一个 objectEventArgs . 有点像方孔和方形钉 . 所以我们说这些方法与委托具有相同的签名或形状 .

    因此,知道如何创建对方法的引用,让我们考虑事件的目的:我们希望在系统中其他地方发生某些事情时执行某些代码 - 或者“处理事件” . 为此,我们为要执行的代码创建特定方法 . 事件和要执行的方法之间的 Binders 是委托 . 事件必须在内部存储指向引发事件时要调用的方法的指针的“列表” . *当然,为了能够调用方法,我们需要知道要传递给它的参数!我们使用委托作为事件和将被调用的所有特定方法之间的“ Contract ” .

    所以默认的 EventHandler (和许多人一样)表示特定的方法形状(同样,void / object-EventArgs) . 当您声明一个事件时,您通过指定一个委托来说明该事件将调用哪种形式的方法(EventHandler):

    //This delegate can be used to point to methods
    //which return void and take a string.
    public delegate void MyEventHandler(string foo);
    
    //This event can cause any method which conforms
    //to MyEventHandler to be called.
    public event MyEventHandler SomethingHappened;
    
    //Here is some code I want to be executed
    //when SomethingHappened fires.
    void HandleSomethingHappened(string foo)
    {
        //Do some stuff
    }
    
    //I am creating a delegate (pointer) to HandleSomethingHappened
    //and adding it to SomethingHappened's list of "Event Handlers".
    myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
    
    //To raise the event within a method.
    SomethingHappened("bar");
    

    (*这是.NET中事件的关键,剥夺了“神奇” - 事件实际上是一个相同“形状”的方法列表 . 列表存储在事件所在的位置 . 事件被“提升”,它实际上只是“通过这个方法列表并调用每个方法,使用这些值作为参数” . 分配事件处理程序只是将方法添加到此方法列表中的更简单,更简单的方法被称为) .

  • 38

    这实际上是事件处理程序的声明 - 一个在触发事件时将被调用的方法 . 要创建一个事件,你可以这样写:

    public class Foo
    {
        public event EventHandler MyEvent;
    }
    

    然后你可以订阅这样的事件:

    Foo foo = new Foo();
    foo.MyEvent += new EventHandler(this.OnMyEvent);
    

    使用OnMyEvent()定义如下:

    private void OnMyEvent(object sender, EventArgs e)
    {
        MessageBox.Show("MyEvent fired!");
    }
    

    只要 Foo 触发 MyEvent ,就会调用 OnMyEvent 处理程序 .

    您并不总是必须使用 EventArgs 的实例作为第二个参数 . 如果要包含其他信息,可以使用从 EventArgs 派生的类(按惯例, EventArgs 是基数) . 例如,如果您查看WinForms中 Control 上定义的某些事件或WPF中的 FrameworkElement ,您可以查看将更多信息传递给事件处理程序的事件示例 .

  • 2

    只是为了在这里添加现有的优秀答案 - 在已接受的代码中构建,使用 delegate void MyEventHandler(string foo) ...

    因为编译器知道SomethingHappened事件的委托类型,所以:

    myObj.SomethingHappened += HandleSomethingHappened;
    

    完全等同于:

    myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);
    

    处理程序也可以使用 -= 取消注册,如下所示:

    // -= removes the handler from the event's list of "listeners":
    myObj.SomethingHappened -= HandleSomethingHappened;
    

    为了完整起见,可以像这样完成事件,只在拥有事件的类中:

    //Firing the event is done by simply providing the arguments to the event:
    var handler = SomethingHappened; // thread-local copy of the event
    if (handler != null) // the event is null if there are no listeners!
    {
        handler("Hi there!");
    }
    

    需要处理程序的线程本地副本以确保调用是线程安全的 - 否则线程可以在我们检查它是否为 null 之后立即取消注册该事件的最后一个处理程序,并且我们将在那里有"fun" NullReferenceException .


    C#6为这种模式引入了一个很好的简短手 . 它使用空传播运算符 .

    SomethingHappened?.Invoke("Hi there!");
    
  • 3

    我对事件的理解是;

    Delegate:

    用于保存对要执行的方法/方法的引用的变量 . 这使得传递变量等方法成为可能 .

    Steps for creating and calling the event:

    • 该事件是委托的实例

    • 由于事件是委托的实例,因此我们必须首先定义委托 .

    • 分配事件触发时要执行的方法/方法(调用委托)

    • 触发事件(呼叫代理人)

    Example:

    using System;
    
    namespace test{
        class MyTestApp{
            //The Event Handler declaration
            public delegate void EventHandler();
    
            //The Event declaration
            public event EventHandler MyHandler;
    
            //The method to call
            public void Hello(){
                Console.WriteLine("Hello World of events!");
            }
    
            public static void Main(){
                MyTestApp TestApp = new MyTestApp();
    
                //Assign the method to be called when the event is fired
                TestApp.MyHandler = new EventHandler(TestApp.Hello);
    
                //Firing the event
                if (TestApp.MyHandler != null){
                    TestApp.MyHandler();
                }
            }
    
        }   
    
    }
    
  • 11

    我同意KE50,除了我将'event'关键字视为'ActionCollection'的别名,因为该事件包含要执行的操作集合(即委托) .

    using System;
    
    namespace test{
    
    class MyTestApp{
        //The Event Handler declaration
        public delegate void EventAction();
    
        //The Event Action Collection 
        //Equivalent to 
        //  public List<EventAction> EventActions=new List<EventAction>();
        //        
        public event EventAction EventActions;
    
        //An Action
        public void Hello(){
            Console.WriteLine("Hello World of events!");
        }
        //Another Action
        public void Goodbye(){
            Console.WriteLine("Goodbye Cruel World of events!");
        }
    
        public static void Main(){
            MyTestApp TestApp = new MyTestApp();
    
            //Add actions to the collection
            TestApp.EventActions += TestApp.Hello;
            TestApp.EventActions += TestApp.Goodbye;
    
            //Invoke all event actions
            if (TestApp.EventActions!= null){
                //this peculiar syntax hides the invoke 
                TestApp.EventActions();
                //using the 'ActionCollection' idea:
                // foreach(EventAction action in TestApp.EventActions)
                //     action.Invoke();
            }
        }
    
    }   
    
    }
    

相关问题