简体   繁体   English

为什么要使用事件来处理委托?

[英]Why use events for what I can do with Delegates?

I know Events are always associated with Delegates.我知道事件总是与代表相关联。 But, I am missing some core use of Events, and trying to understand that.但是,我缺少事件的一些核心用途,并试图理解这一点。

I created a simple Event program, as below, and it works perfectly fine.我创建了一个简单的事件程序,如下所示,它运行良好。

namespace CompleteRef3._0
{
delegate void someEventDelegate();

class EventTester
{
    public event someEventDelegate someEvent;

    public void doEvent()
    {
        if (someEvent != null) someEvent();
    }

}

class Program
{
    static void EventHandler1()
    {
        Console.WriteLine("Event handler 1 called..");
    }

    static void EventHandler2()
    {
        Console.WriteLine("Event handler 2 called..");
    }
    static void EventHandler3()
    {
        Console.WriteLine("Event handler 3 called..");
    }


    static void Main(string[] args)
    {
        EventTester evt = new EventTester();
        evt.someEvent += EventHandler1;
        evt.someEvent += EventHandler2;
        evt.someEvent += EventHandler3;
        evt.doEvent();
        Console.ReadKey();

    }
}
}

I replaced the event declaration with delegates.我用委托替换了事件声明。 That is I replaced the line public event someEventDelegate someEvent;那就是我替换了行public event someEventDelegate someEvent; with someEventDelegate someEvent;someEventDelegate someEvent; on the above program, and I still get the same result.在上面的程序中,我仍然得到相同的结果。 Now, I was confused why we need to use Events, if it can be achieved by Delegates only.现在,我很困惑为什么我们需要使用事件,如果它只能由委托来实现。 What is the real use of Events?事件的真正用途是什么?

The modified program without events is as below -修改后的没有事件的程序如下——

namespace CompleteRef3._0
{
delegate void someEventDelegate();

class EventTester
{
    someEventDelegate someEvent;

    public void doEvent()
    {
        if (someEvent != null) someEvent();
    }

}

class Program
{
    static void EventHandler1()
    {
        Console.WriteLine("Event handler 1 called..");
    }

    static void EventHandler2()
    {
        Console.WriteLine("Event handler 2 called..");
    }
    static void EventHandler3()
    {
        Console.WriteLine("Event handler 3 called..");
    }


    static void Main(string[] args)
    {
        EventTester evt = new EventTester();
        evt.someEvent += EventHandler1;
        evt.someEvent += EventHandler2;
        evt.someEvent += EventHandler3;
        evt.doEvent();
        Console.ReadKey();

    }
}
}

Imagine you have 3 subscribers who are interested in your someEvent.假设您有 3 个订阅者对您的 someEvent 感兴趣。 Let's further imagine they are interested in receiving events from the same EventTester instance.让我们进一步想象他们有兴趣从同一个EventTester实例接收事件。 For brevity, let's leave out the details of how the exact same instance is passed to all the clients.为简洁起见,让我们忽略如何将完全相同的实例传递给所有客户端的细节。 When I say clients , I mean any class who is a subscriber to the event.当我说clients 时,我的意思是任何订阅事件的类。

Here is the instance:这是实例:

EventTester evt = new EventTester();

They have subscribed to the event of the above instance as shown below:他们订阅了上述实例的事件,如下所示:

Client 1客户 1

evt.someEvent += Client1Handler1;
evt.someEvent += Client1Handler2;

Client 2客户 2

evt.someEvent += Client2Handler1;

Client 3客户 3

evt.someEvent += Client3Handler1;
evt.someEvent += Client3Handler2;

Client 4:客户 4:

Imagine client 4 did one of the 3 below:想象一下,客户端 4 执行了以下 3 项之一:

// 1: See Note 1 below
evt.someEvent = null;

// 2: See Note 2 below
evt.someEvent = new someEventDelegate(MyHandler);

// 3: See Note 3 below
evt.someEvent();

//...
private void MyHandler()
{
    MessageBox.Show("Client 4");
}

Note 1注 1

Client 1, 2, and 3 will not be getting any events anymore.客户端 1、2 和 3 将不再接收任何事件。 Why?为什么? Because Client 4 just did this evt.someEvent = null;因为客户端 4 刚刚做了这个evt.someEvent = null; and in EventTester you have this line of code:EventTester您有以下代码行:

if (someEvent != null) someEvent();

Since that condition will not pass anymore, no event will be raised.由于该条件不会再通过,因此不会引发任何事件。 There is nothing wrong with the above line of code by the way.顺便说一下,上面这行代码没有任何问题。 But there is the problem with using delegates: It can be assigned to.但是使用委托存在一个问题:它可以分配给。

Note 2笔记2

It has been completely over-written to a brand new instance.它已被完全覆盖到一个全新的实例。 Now regardless of the client, they will all see a message box that says "Client 4".现在不管是哪个客户端,他们都会看到一个消息框,上面写着“客户端 4”。

Note 3注 3

Ooops all of a sudden one of the clients is broadcasting the event.哎呀,突然间,其中一个客户端正在广播该事件。


Imagine for a second EventTester was a Button and someEvent was Click .想象第二个EventTester是一个ButtonsomeEventClick Imagine you had multiple clients interested in the click event of this button.想象一下,您有多个客户对此按钮的点击事件感兴趣。 All of a sudden, one client decides no-one else should get notifications (Note 1).突然之间,一个客户端决定没有其他人应该收到通知(注 1)。 Or one client decides that when the button is clicked, it will be handled only 1 way (Note 2).或者一个客户端决定当按钮被点击时,它只会被处理 1 种方式(注 2)。 Or it has made the decision that it will decide when a button is clicked even though the button may not have been clicked (Note 3).或者它已经决定它将决定何时单击按钮,即使该按钮可能尚未被单击(注 3)。


If you have an event and one of the clients tried the above, they will not be allowed and get a compile error, like this:如果您有一个event并且其中一个客户端尝试了上述操作,则它们将不被允许并出现编译错误,如下所示:

在此处输入图片说明

Sure, you can use delegates because behind the scenes an event is a construct that wraps a delegate.当然,您可以使用委托,因为在幕后,事件是包装委托的构造。

But the rationale of using events instead of delegates is the the same as for using properties instead of fields - data encapsulation .但是使用事件而不是委托的原理与使用属性而不是字段 -数据封装的原理相同。 It's bad practice to expose fields (whatever they are - primitive fields or delegates) directly.直接公开字段(无论它们是什么 - 原始字段或委托)是不好的做法。

By the way, you missed a public keyword before your delegate field to make it possible in the second snippet.顺便说一下,您在委托字段之前错过了一个public关键字,以使其在第二个代码段中成为可能。

Another "by the way" with the second snippet: for delegates you should use Delegate.Combine instead of "+=".第二个片段的另一个“顺便说一下”:对于代表,您应该使用Delegate.Combine而不是“+=”。

The main purpose of events is to prevent subscribers from interfering with each other.事件的主要目的是防止订阅者相互干扰。 If you do not use events, you can:如果不使用事件,则可以:

Replace other subscribers by reassigning delegate(instead of using the += operator), Clear all subscribers (by setting delegate to null), Broadcast to other subscribers by invoking the delegate.通过重新分配委托(而不是使用 += 运算符)替换其他订阅者,清除所有订阅者(通过将委托设置为 null),通过调用委托向其他订阅者广播。

Source: C# in a Nutshell来源:C# 简而言之

public class Program
{
    public static void Main()
    {
        Number myNumber = new Number(100000);
        myNumber.PrintMoney();
        myNumber.PrintNumber();
        Console.ReadKey();
    }
}

public class Number
{
    private PrintHelper _printHelper;

    public Number(int val)
    {
        _value = val;

        _printHelper = new PrintHelper();
        //subscribe to beforePrintEvent event
        _printHelper.beforePrintEvent += printHelper_beforePrintEvent;
    }
    //beforePrintevent handler
    void printHelper_beforePrintEvent(string message)
    {
        Console.WriteLine("BeforePrintEvent fires from {0}", message);
    }

    private int _value;

    public int Value
    {
        get { return _value; }
        set { _value = value; }
    }

    public void PrintMoney()
    {
        _printHelper.PrintMoney(_value);
    }

    public void PrintNumber()
    {
        _printHelper.PrintNumber(_value);
    }

}
public class PrintHelper
{
    public delegate void BeforePrintDelegate(string message);
    public event BeforePrintDelegate beforePrintEvent;

    public PrintHelper()
    {

    }

    public void PrintNumber(int num)
    {
        if (beforePrintEvent != null)
            beforePrintEvent.Invoke("PrintNumber");

        Console.WriteLine("Number: {0,-12:N0}", num);

    }

    public void PrintDecimal(int dec)
    {
        if (beforePrintEvent != null)
            beforePrintEvent("PrintDecimal");

        Console.WriteLine("Decimal: {0:G}", dec);
    }

    public void PrintMoney(int money)
    {
        if (beforePrintEvent != null)
            beforePrintEvent("PrintMoney");

        Console.WriteLine("Money: {0:C}", money);
    }

    public void PrintTemperature(int num)
    {
        if (beforePrintEvent != null)
            beforePrintEvent("PrintTemerature");

        Console.WriteLine("Temperature: {0,4:N1} F", num);
    }
    public void PrintHexadecimal(int dec)
    {
        if (beforePrintEvent != null)
            beforePrintEvent("PrintHexadecimal");

        Console.WriteLine("Hexadecimal: {0:X}", dec);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM