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

public void EventName(object sender, EventArgs e);

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


当前回答

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

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

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

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

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

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

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

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

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

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

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

其他回答

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

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

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

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

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

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

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

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

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

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

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

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

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

}   

}

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

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

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

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

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