我理解事件的目的,特别是在创建用户界面的上下文中。我认为这是创建事件的原型:

public void EventName(object sender, EventArgs e);

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


当前回答

只是在这里添加到现有的伟大答案-在已接受的代码的基础上构建,它使用了委托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!");
}

处理程序的线程本地副本是必要的,以确保调用是线程安全的——否则,线程可能会在检查事件是否为空后立即注销该事件的最后一个处理程序,我们将在那里有一个“有趣的”NullReferenceException。


c# 6为这个模式引入了一个很好的简写。它使用空传播操作符。

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

其他回答

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

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上定义的一些事件,你可以看到将附加信息传递给事件处理程序的事件示例。

下面是一个代码示例,可能会有所帮助:

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

发布者:事件发生的地方。发布者应该指定类使用哪个委托,并生成必要的参数,将这些参数和自身传递给委托。

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

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

另一件需要知道的事情是,在某些情况下,当你需要低级别耦合时,你必须使用委托/事件!

如果您想在应用程序的多个地方使用一个组件,您需要使组件具有低级别的耦合,并且必须将特定的无关逻辑委托到组件之外!这确保了您有一个解耦的系统和更清晰的代码。

在SOLID原则中,这是“D”,(依赖倒置原则)。

也被称为“IoC”,反转控制。

你可以用事件、委托和DI(依赖注入)来创建“IoC”。

在子类中访问方法很容易。但是从子类中访问父类中的方法更加困难。你必须把父引用传递给子引用!(或使用DI接口)

委托/事件允许我们在没有引用的情况下从子对象通信到父对象!

在上面的图中,我没有使用委托/事件,并且父组件B必须有父组件a的引用来执行方法a中无关的业务逻辑(高级别耦合)。

使用这种方法,我将不得不放置所有使用组件B的组件的所有引用!:(

在上面的图中,我使用委托/事件,组件B不必知道a(低级别耦合)

您可以在应用程序的任何地方使用组件B !

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