事件与代表及其各自的应用程序之间的区别

我不认为使用事件优于代表,除了语法糖。 也许我是误解,但是事件似乎只是代表的占位符。

你能向我解释一下哪些差异以及何时使用哪一个? 有什么优点和缺点? 我们的代码与事件密切相关,我想深入其中。

你什么时候使用代表,而不是事件,反之亦然? 请在生产代码中说明两者的实际经验。

从技术angular度来看,其他答案已经解决了这些差异。

从语义angular度来看,事件是在满足某些条件时由对象提出的动作。 例如,我的Stock类有一个名为Limit的属性,当股票价格达到Limit时,它会引发一个事件。 这个通知是通过一个事件完成的。 是否有人真正关心这个事件,并赞同这个事件是超出业主阶层的关注。

代表是一个更通用的术语,用C / C ++术语来描述类似于指针的结构。 .Net中的所有代表都是多播代表。 从语义angular度来看,它们通常被用作一种input。 特别是实施战略模式的完美方式。 例如,如果我想对一个List对象sorting,我可以提供一个Comparator策略来告诉实现如何比较两个对象的方法。

我在生产代码中使用了这两种方法。 我的数据对象通知吨什么时候满足某些属性。 最基本的例子是,无论何时属性发生变化,都会引发PropertyChanged事件(请参阅INotifyPropertyChanged接口)。 我用代码中的代表提供了将某些对象转换为string的不同策略。 这个特殊的例子是一个ToString()的特殊对象types的实现列表,以显示给用户。

关键字event是多播委托的范围修饰符。 这个和声明一个多播委托之间的实际区别如下:

  • 您可以在界面中使用event
  • 对多播委托的调用访问仅限于声明类。 行为就好像委托对于调用是私有的。 为了分配的目的,访问是由明确的访问修饰符(例如public event )指定的。

作为感兴趣的问题,您可以将+-应用于多播委托,这是将代表与事件组合分配的+=-=语法的基础。 这三个片段是相同的:

 B = new EventHandler(this.MethodB); C = new EventHandler(this.MethodC); A = B + C; 

样本二,说明直接分配和组合分配。

 B = new EventHandler(this.MethodB); C = new EventHandler(this.MethodC); A = B; A += C; 

示例三:更熟悉的语法。 你可能熟悉null的赋值来删除所有的处理程序。

 B = new EventHandler(this.MethodB); C = new EventHandler(this.MethodC); A = null; A += B; A += C; 

像属性一样,事件有一个完全没有人使用的语法。 这个:

 class myExample { internal EventHandler eh; public event EventHandler OnSubmit { add { eh = Delegate.Combine(eh, value) as EventHandler; } remove { eh = Delegate.Remove(eh, value) as EventHandler; } } ... } 

…和这个完全一样:

 class myExample { public event EventHandler OnSubmit; } 

添加和删​​除方法在VB.NET使用的相当高亢的语法中更加显眼(没有操作符重载)。

事件是语法糖。 它们很美味。 当我看到一个事件,我知道该怎么做。 当我看到一个代表时,我不太确定。

结合事件和界面(更多的糖)使得一个令人垂涎的小吃。 代表和纯粹的虚拟抽象类远不如美食。

事件在元数据中被标记为这样。 这允许像Windows窗体或ASP.NETdevise器这样的事件与委托types的单纯属性区分开来,并为它们提供适当的支持(特别是在“属性”窗口的“事件”选项卡上显示它们)。

与委托types属性的另一个区别是用户只能添加和删除事件处理程序,而具有委托types的属性则可以设置值:

 someObj.SomeCallback = MyCallback; // okay, replaces any existing callback someObj.SomeEvent = MyHandler; // not okay, must use += instead 

这有助于隔离事件订阅者:我可以将事件添加到事件中,并且可以将您的事件处理程序添加到同一个事件中,而且不会意外地覆盖我的处理程序。

编辑#1 什么时候你会使用代表通过事件和vs.versa? 请在生产代码中说明两者的实际经验。

当我devise我自己的API时,我定义了作为parameter passing给方法的委托,或者定义类的构造函数:

  • 所以一个方法可以实现一个简单的“模板方法”模式(例如PredicateAction委托被传递给.Net通用集合类)
  • 或者让类可以做一个“callback”(通常是创build它的类的方法的callback)。

这些委托在运行时通常是非可选的 (即不能为null )。

我倾向于不使用事件; 但我在哪里使用事件,我使用它们可选地将事件信号发送给可能感兴趣的零个,一个或多个客户端,即,当类(例如System.Windows.Form类)应该存在并运行时是否有意义或者没有任何客户端为其事件添加事件处理程序(例如,表单的“鼠标向下”事件存在,但是任何外部客户端是否有兴趣在该事件上安装事件处理程序是可选的 )。

尽pipe事件通常是通过多播委托实现的,但并不要求以这种方式使用事件。 如果一个类暴露事件,那就意味着这个类暴露了两个方法。 其实质是:

  1. 这是一个代表。 请在有趣的事情发生时调用它。
  2. 这是一个代表。 你应该尽快销毁所有对它的引用(不再叫它)。

一个类处理一个事件的最常见的方式就是定义一个多播委托,并添加/删除所有传递给上述方法的委托,但并不要求它们以这种方式工作。 不幸的是,事件架构没有做一些事情,使替代方法更清洁(例如,订阅方法返回一个MethodInvoker,这将由订阅者保留;取消订阅一个事件,只是调用返回的方法),所以多播委托是迄今为止最常用的方法。

了解不同你可以看看这2个例子

用委托来实现(这种情况下的动作是一种不返回值的委托)

 public class Animal { public Action Run {get; set;} public void RaiseEvent() { if (Run != null) { Run(); } } } 

使用委托你应该做这样的事情

 Animale animal= new Animal(); animal.Run += () => Console.WriteLine("I'm running"); animal.Run += () => Console.WriteLine("I'm still running") ; animal.RaiseEvent(); 

这个代码运行良好,但你可能有一些弱点。

例如,如果我写这个

 animal.Run += () => Console.WriteLine("I'm running"); animal.Run += () => Console.WriteLine("I'm still running"); animal.Run = () => Console.WriteLine("I'm sleeping") ; 

与最后一行代码,我已经覆盖了以前的行为只是一个+ (我用+而不是+=

另一个弱点是每个使用Animal类的类都可以调用RaiseEvent只是把它animal.RaiseEvent()

为了避免这个弱点,你可以在c#中使用events

你的动物课将以这种方式改变

 public class ArgsSpecial :EventArgs { public ArgsSpecial (string val) { Operation=val; } public string Operation {get; set;} } public class Animal { public event EventHandler<ArgsSpecial> Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it public void RaiseEvent() { Run(this, new ArgsSpecial("Run faster")); } } 

呼叫事件

  Animale animal= new Animal(); animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation); animal.RaiseEvent(); 

区别:

  1. 您没有使用公共属性,而是使用公共字段(编译器保护您的字段免受不必要的访问)
  2. 事件不能直接分配。 在这种情况下,你不能做覆盖行为的前面的错误。
  3. 你们class以外的人不能举办这个活动。
  4. 事件可以被包含在一个接口声明中,而一个字段则不能

笔记

EventHandler被声明为以下代理:

 public delegate void EventHandler (object sender, EventArgs e) 

它需要一个发送者(对象types)和事件参数。 发送方如果来自静态方法,则为空。

你也可以使用EventHAndler代替这个使用EventHandler<ArgsSpecial>例子

请参阅此处了解有关EventHandler的文档

虽然我没有任何技术上的理由,但我使用UI风格代码中的事件,换句话说,在代码的更高层次中,使用委托代表更深层次的代码。 正如我所说,你可以使用,但我觉得这使用模式是合理的,如果没有别的,它有助于loggingcallbacktypes和他们的层次结构。


编辑:我认为我的使用模式的差异是,我觉得完全可以接受忽略事件,他们是挂钩/存根,如果你需要了解事件,听他们,如果你不在乎事件只是忽略它。 这就是为什么我使用他们的UI,Javascript /浏览器事件样式。 但是,当我有一个委托,我期望真正期望有人来处理委托的任务,并抛出一个exception,如果不处理。

事件和代表之间的差异比我以前想的要小得多..我刚刚发布了一个关于这个主题的超短videoYouTube: https : //www.youtube.com/watch?v=el-kKK-7SBU

希望这可以帮助!