[英]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
是一个Button
而someEvent
是Click
。 想象一下,您有多个客户对此按钮的点击事件感兴趣。 突然之间,一个客户端决定没有其他人应该收到通知(注 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.