事件 vs 事件处理程序

12

我阅读了很多材料,但仍然不理解,请有人给我解释一下。

我知道的:

声明一个事件:

public event MyEvent myEvent;

Vs.

声明 EventHandler:

public EventHandler MyEventHandler;

虽然 EventHandler 是 .NET 中的关键字:

public delegate void EventHandler (Send object, EventArgs e);

因为在关键字“事件”中未使用它,所以EventHandler是委托而不是事件?

那么何时应该使用“Event”,何时应该使用“EventHandler”?


5
事件(event)是触发事件的事物,有其他东西可以订阅它。事件处理程序(EventHandler)是处理事件的东西 - 即指定用于订阅事件的方法的对象。” - Matthew Watson
2
这里已经很好地解释了:https://learn.microsoft.com/en-us/dotnet/standard/events/ - jeroenh
1
一个事件有一个类型,它描述了在触发事件时应该传递哪些参数以及事件处理程序必须具备哪些参数才能与事件兼容。这个类型由委托声明描述。EventHandler是其中之一(不是关键字),由框架提供。它规定事件处理程序必须有2个参数,大家都称它们为sender和e。MyEvent是另一个由您提供的事件,我们看不到它。使用框架声明是一个好主意,它使您的代码易于阅读。 - Hans Passant
2
你越来越接近了。你声明了一个简单事件,仅此而已。那不是委托,那个来自框架。事件的行为很像属性。你使用属性来限制对私有字段的访问,外部代码必须使用get和set访问器。类似地,事件限制对私有委托对象的访问,外部代码只能添加和移除事件处理程序。外部代码无法触发事件,无法强制将委托对象设置为null,也无法取消注册由其他代码注册的处理程序。限制访问是一件好事。 - Hans Passant
1
虽然 EventHandler 是 .NET 中的关键字,但实际上它不是。在你的代码中,MyEvent 和 EventHandler 都是委托类型,而 MyEventHandler 则是一个委托实例。 - H H
显示剩余3条评论
3个回答

22
啊,事件 vs. 委托的问题。我也记得曾经有过这个问题...
所以,假设你正在创建一个名为 "Cat" 的类,并且想让人们知道你的猫什么时候饿了。你可以通过两种方式之一来实现:通过在 Cat 类上公开委托或事件。
您可以将委托视为指向函数(或方法)的指针。因此,假设有一个名为 "Person" 的类,其中有一个名为 FeedCat(Cat cat) 的方法。
委托方式
如果您的猫正在公开一个名为 HungryDelegate 的委托,则该人可以将委托指向其 FeedCat 方法,以便当猫饿时,它有一种调用该人 FeedCat 方法的方式。
问题在于,只有一个人可以喂猫。假设您希望多个人能够喂猫。人们可能会忙于做其他事情,因此重要的是让猫能够告诉多个人它饿了。这样,人们就会收到通知,并在有机会时检查猫的情况,看看是否已经有人喂了猫,如果没有则喂它。
事件解救:
事件基本上是一个委托列表(特定类型的)。如果在您的 "Cat" 类上公开了一个 Hungry 事件,则多个人可以添加指向其 FeedCat 方法的指针(委托)。
他们也可以删除他们想要的 FeedCat 方法的指针(委托)。假设一个人搬走了,现在是他们的姐妹在照顾猫。该人可以从猫中删除他们自己的 FeedCat 函数的委托,以便他们不再收到关于这只可恶的猫饿了的通知。
事件 vs. 多路广播委托?

从技术上讲,为了能够提供多个委托者,您可以使用所谓的MultiCastDelegates而不是事件。它们是组合委托(委托的链接列表)。问题在于,任何人都可以从外部操纵它们。一个邪恶的人可能会删除其他所有人的“FeedCat”委托,可怜的猫将会挨饿(或不得不学会打猎)。

使用事件时重要的一点是,该人无法看到添加到事件中的其他人的委托,并且不能删除它们或与其交互(原则上)。


1
监听器 = 代理,观察者 = 事件 - Maor Hadad

11

公开一个 (public) 字段

 public EventHandler MyEventHandler;

这是一种不好的实践:一个小的打字错误就可以轻易地破坏代码:

 MyClass demo = new MyClass();

 demo.MyEventHandler += MyMethod;

 ...

 // Can you see the error? = instead of correct += ?
 // MyMethod will not be called since this assignment 
 demo.MyEventHandler = MyReaction;  

这就是为什么你应该使用专门为此设计的事件(event)
 public event EventHandler MyEventHandler;

如果我们尝试之前的 demo,将会得到编译时错误

 MyClass demo = new MyClass();

 ...

 demo.MyEventHandler = MyReaction; // <- doesn't compile, insist on +=

在极少数情况下,您可能需要显式委托字段,但这些字段应该被隐藏,例如声明为 private 而不是 public

// We don't expose the field
private EventHandler m_MyEventHandler;

// ... But event:
public event EventHandler MyEventHandler {
  add {
    //TODO: extra logic on += operation

    m_MyEventHandler += value;
  }
  remove {
    //TODO: extra logic on -= operation

    m_MyEventHandler -= value;
  }  
} 

第三个代码片段(在“这就是为什么您应该使用专门为此设计的_event_”之后)可能应该是 public event EventHandler MyEventHandler; 即应添加 event 关键字(否则它与第一个代码片段完全相同)。 - striving_coder

1

委托,事件(事件处理程序/事件监听器),概念(多播/广播),Action和Func

这将是一篇长文,但它是最简单的解释。这个主题很烦人的原因是因为人们只是用不同的词来解释同样的事情。

首先,你应该知道一些东西:

委托:它仅仅是一个方法列表,为什么要创建一个列表?因为当你的代码被执行时,该列表被提取并且其中的每个方法都会被一个接一个地执行,只需不听教科书上的定义,掌握这个,你就没问题了。

也称为:

  • 函数指针
  • 可以像变量一样发送和接收方法的方法包装器

要创建一个委托,你需要使用:

[[access modifier] delegate [return type] [delegate name]([parameters])]

example: public delegate int demo(int a);

现在要执行存储在名为delegate的列表中的所有方法,您可以继续。
1. demo.invoke(a);
2. demo(a);   ..... both are valid

使用点号和显式调用在异步编程中很常见,你可以使用beginInvoke,但这超出了本主题的范围。
还有一件事叫做“创建委托对象/实例化委托”,它基本上就像它听起来的那样,但为了避免混淆,它是这样的(针对上面的示例)。
example : demo del = new demo(); (or) Public demo del = null;

要将任何方法添加到名为delegate的列表中,您需要使用+=,并且还需要在“方法要求满足”后将其删除,使用-=。
(方法的要求得到满足意味着您不再需要该方法处于活动状态或称为“侦听”)。如果您不删除它,可能会导致“内存泄漏”,这意味着您计算机的RAM将被耗尽,技术上分配的内存将不会释放。
例如:假设有一个方法。
public int calculate (int c)

to add this method to delegate you go

1.  del = calculate;
2.  del += calculate; .... all are valid

to remove 

del -= calculate

首先请注意委托和方法之间的相似之处,返回类型(输出)和输入/参数是相同的,这是一个规则,您不能在委托中添加任何随意的或一堆方法,它需要遵循输入-输出规则。
现在为什么有两种不同的方式来做同一件事,唯一不同的是赋值运算符(+,=),这引入了一个新主题,称为事件。
事件只是委托的受限版本,仍然是一系列方法,不要因为人们解释这些术语而混淆,他们会更改名称,因此坚持使用这个术语来理解。
什么是约束?你不能这样做del = calculate;。有什么危害呢?假设一堆方法被添加到委托(列表)中,你这样做,所有方法都将被清除,只剩下一个方法“calculate”,所以为了防止这种情况,使用事件。
事件语法:
公共事件演示del = null;
还有一件事情,您不能像demo.invoke那样直接调用委托,因为它是公共的,可以访问和调用,但是对于事件,它不能。
现在,您只需将方法添加到事件(一种特殊类型的委托)即可。

何时使用事件和委托,取决于您的情况,但实际上事件很受欢迎。

更多关键词:

多播:只是将多个方法添加到委托中 广播:向事件添加多个方法

发布者:执行该方法的人(在广播中使用的术语),仅有一个实体 订阅者:正在执行的方法,可以是多个

监听器:与订阅者相同,但术语用于多播

事件处理程序:与订阅者/事件侦听器相同,那么有什么区别?基本上是相同的东西,有些人说事件侦听器检测事件发生,而事件处理程序“处理”或执行代码,它们在实践中是相同的!

Action和Func只是已创建并实例化的委托,因此在一个单词中有2行代码,区别仅在于返回类型

ACTION:不返回任何内容,但输入为0个或1个以上

FUNC:返回一个内容并带有参数

如果您不擅长阅读,这里是有关此主题的最佳视频

https://www.youtube.com/playlist?list=PLFt_AvWsXl0dliMtpZC8Qd_ru26785Ih_


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接