9

我知道事件总是与代表相关联。但是,我错过了事件的一些核心用途,并试图理解这一点。

我创建了一个简单的事件程序,如下所示,它工作得非常好。

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

    }
}
}

我用代表替换了事件声明。那就是我替换了行public event someEventDelegate someEvent; someEventDelegate someEvent; 在上面的程序上,我仍然得到相同的结果。现在,我很困惑为什么我们需要使用事件,如果它只能由代表来实现。事件的真正用途是什么?

没有事件的修改程序如下 -

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

    }
}
}
4

4 回答 4

18

假设您有 3 个订阅者对您的 someEvent 感兴趣。让我们进一步假设他们有兴趣从同一EventTester实例接收事件。为简洁起见,让我们忽略如何将完全相同的实例传递给所有客户端的细节。当我说clients时,我指的是任何订阅该事件的类。

这是实例:

EventTester evt = new EventTester();

他们已经订阅了上述实例的事件,如下所示:

客户 1

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

客户 2

evt.someEvent += Client2Handler1;

客户 3

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

客户 4:

想象一下客户端 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");
}

注1

客户端 1、2 和 3 将不再收到任何事件。为什么?因为客户端 4 刚刚执行此操作evt.someEvent = null;,并且EventTester您有以下代码行:

if (someEvent != null) someEvent();

由于该条件将不再通过,因此不会引发任何事件。顺便说一句,上面这行代码没有任何问题。但是使用委托有一个问题:它可以被分配给。

笔记2

它已被完全覆盖到一个全新的实例中。现在无论客户端如何,他们都会看到一个显示“客户端 4”的消息框。

注3

哎呀,突然之间,其中一个客户正在广播该事件。


想象一下EventTester是 aButtonsomeEventwas Click。假设您有多个客户对此按钮的点击事件感兴趣。突然之间,一个客户决定没有其他人应该收到通知(注 1)。或者一个客户决定当按钮被点击时,它只会被处理一种方式(注2)。或者它已经决定它会决定何时单击按钮,即使该按钮可能尚未被单击(注 3)。


如果您有一个event和一个客户端尝试了上述操作,他们将被禁止并获得编译错误,如下所示:

在此处输入图像描述

于 2016-12-23T18:09:35.580 回答
10

当然,您可以使用委托,因为在幕后,事件是包装委托的构造。

但是使用事件而不是委托的基本原理与使用属性而不是字段数据封装的基本原理相同。直接公开字段(无论它们是什么 - 原始字段或委托)是不好的做法。

顺便说一句,您public在委托字段之前错过了一个关键字,以便在第二个片段中使用它。

第二个片段的另一个“顺便说一句”:对于代表,您应该使用Delegate.Combine而不是“+=”。

于 2013-03-03T06:24:09.873 回答
8

事件的主要目的是防止订阅者相互干扰。如果您不使用事件,您可以:

通过重新分配委托替换其他订阅者(而不是使用 += 运算符),清除所有订阅者(通过将委托设置为 null),通过调用委托向其他订阅者广播。

资料来源:简而言之 C#

于 2013-03-03T06:21:47.417 回答
0
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);
    }
}
于 2019-10-19T14:09:52.610 回答