我最近才知道C#'事件'确实是。老实说,这不是真的。总结我的发现: 事件关键字只是一个仅适用于代表的修饰符。
因此,事件的所有“魔力”都是代表的操作。而已。我已阅读了很多Microsoft文档,但没有任何句子如此简洁地以这种方式进行总结。为了继续我的发现,委托,类和结构都处于相同的“级别”。它们是定义“对象”的方法。我并不是指类型中的“对象”,而是“某事”的封装概念。就像说面向对象编程时使用“对象”这个词一样。
无论如何,'对象'有某些修饰符。例如,密封,只读,虚拟,静态等...... 可以找到此列表here。对于委托,它有一个名为 event 的额外的委托。事件使得当委托被声明为类的一部分时,它仅根据给予事件的访问修饰符公开添加和删除方法。这些方法的定义与属性的 get 和 set 类似。委托的其他操作(赋值,读访问,方法调用等)仅允许在声明事件委托的类中。我感兴趣的另一件事是,所有委托都有方法Invoke,BeginInvoke和EndInvoke,但你无法导航到在Visual Studio中查看它们,也无法找到描述它们的文档......
好。所以在了解了所有这些之后,使用event关键字的优势除了修改代理的访问方式之外还有什么好处?在很多情况下,我觉得最好只是声明一个没有event关键字的委托。我最近遇到的一种情况是我想创建一个包含2个事件的抽象基类。从这个基类派生的任何类都应该能够使用类似于它们自己的事件,类似于暴露给派生类的类的任何其他对象(也就是非私有的,除非派生类在另一个程序集,并且对象被声明为内部)。
基本上,我希望派生类将这些事件用作自己的事件。执行此操作的唯一方法是将事件的支持变量公开为受保护,因此派生类可以引发事件。看看代码,这似乎很愚蠢,因为我基本上定义了两次委托;曾经作为一个受保护的领域,另一个作为公共事件。我想,
创建一个名为Event的类不会更好 构造函数中Action的参数?返回的操作等同于许多人作为委托的扩展方法所做的提升,它检查委托是否为空,然后调用委托。 Event上唯一的公共方法是Add和Remove,用于追加委托并从底层委托中删除它们(+ =, - =)。类可以将这些事件作为属性,例如
public Event SomethingHappened { get; private set; }
这样只有该类可以重新分配事件。或者公共只读字段也同样有效。从构造函数返回的out参数由类存储,并在类想要引发事件时调用。我知道这是一个很好的解决方法,但它会完成工作,并允许事件不仅作为参数传递,而且如果基类将其定义为受保护,则允许派生类调用Raise方法。
TLDR:
答案 0 :(得分:20)
除了修改代理的访问方式外,使用event关键字有什么好处?
是使用event关键字的主要优势。您只使用一个事件而不是一个原始委托,以防止从其定义的类的范围之外调用或清除该委托,因为在事件的情况下,该类负责调用该事件。外部实体不应该直接调用它(它们可以而且应该间接地调用事件),也不应该“关心”是否有任何其他事件处理程序或涉及触摸它们(例如,通过完全分配它们)新的代表到该领域)。
希望允许子类触发事件的特定情况通常通过让定义事件的类创建一个除了触发事件之外什么也不做的受保护方法来解决。按照惯例,此类方法将与事件具有相同的名称,但前缀为“On”。
是的,您可以创建自己的类型,逻辑上表示事件,是委托的包装器,并将可以对该事件执行的功能限制为“应该”执行它们的功能(可能稍微使用)与C#event
关键字使用的规则不同。这是其他语言中经常使用的,没有event
关键字(甚至可能是委托).C#设计者只是意识到这是这是一种非常常见的模式,并认为将关键字添加到语言中以帮助最小化创建逻辑“事件”所需的样板代码是值得的。
使用event
关键字的另一个好处是,只需要将某种类型的委托作为属性,就可以使您的意图更加清晰。如果我只看到一个委托属性,那么暗示通常表示它代表一种方法。是的,C#中的所有代表都是多播代理,因此不是这样,但人们在事件之外利用该功能是不寻常的。人们认为 Action
表示一个操作,而不是一个操作列表。事件对C#文档也有特殊处理。它们都是单独列出的,它们在视觉工作室中有不同的图标等。这一切都有助于使成员的意图和语义更加清晰,使用该课程的人一目了然。
最后,event
关键字确保多个线程之间存在同步,这不是由Delegate
类执行的。如果多个线程同时向事件添加处理程序,event
关键字将确保添加两者。如果你只是公开公开一个委托,那么由于竞争条件而有人可能会覆盖另一个委托,并且最终会有一个处理程序被丢弃。如果你推出自己的Event
类,你可以提供这个功能,但它更像是样板代码,而且非常容易搞乱(导致竞争条件失败,或过度同步导致性能下降)
答案 1 :(得分:3)
基本上,我希望派生类将这些事件用作自己的事件。执行此操作的唯一方法是将事件的支持变量公开为protected,因此派生类可以引发事件。
处理此问题的常用方法不是公开字段,而是公开引发事件的方法。
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
var handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
这不仅允许派生类引发事件,而且允许派生类在任何订阅处理程序实际被调用之前执行某些操作。
回答你的实际问题:
除了修改代理的访问方式外,使用event关键字有什么好处?
尚未提及的一个优点(我认为):
public event PropertyChangedEventHandler PropertyChanged;
可以更改为
public event PropertyChangedEventHandler PropertyChanged
{
add { /* custom code here */ }
remove { /* custom code here */ }
}
无需重新编译库的所有用户。如果您稍后找到不将处理程序存储在私有字段中的理由,您可能需要这样的东西。这与字段上自动实现的属性相同。
答案 2 :(得分:0)
我认为您可以轻松地比较事件'访问者的关键字,但您还有其他一些好处来自' event'关键字: