繁体   English   中英

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

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

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

我创建了一个简单的事件程序,如下所示,它运行良好。

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

    }
}
}

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


如果您有一个event并且其中一个客户端尝试了上述操作,则它们将不被允许并出现编译错误,如下所示:

在此处输入图片说明

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

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

顺便说一下,您在委托字段之前错过了一个public关键字,以使其在第二个代码段中成为可能。

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

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

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

来源: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