代表和事件之间有什么区别?

代表和事件之间有什么区别? 不要同时持有可执行函数的引用吗?

事件声明在委托实例上添加一层抽象和保护。 此保护可防止委托的客户端重置委托及其调用列表,并只允许从调用列表中添加或删除目标。

除了语法和操作属性外,还有语义上的差异。

代表们在概念上是function模板; 也就是说,他们expression了一个function必须遵守的合同,才能被视为代表的“types”。

事件代表…事件。 他们的目的是在某事发生时提醒某人,是的,他们坚持一个委托定义,但他们不是一回事。

即使他们是完全一样的东西(语法和IL代码)仍然会保持语义上的差异。 一般来说,我更喜欢为两个不同的概念使用两个不同的名称,即使它们以相同的方式实现(并不意味着我喜欢使用相同的代码两次)。

要理解这些差异,你可以看看这两个例子

代表示例(在这种情况下,一个Action – 这是一种不返回值的委托)

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

要使用委托,你应该这样做:

 Animal 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 { // Empty delegate. In this way you are sure that value is always != null // because no one outside of the class can change it. public event EventHandler<ArgsSpecial> Run = delegate{} public void RaiseEvent() { Run(this, new ArgsSpecial("Run faster")); } } 

呼叫事件

  Animal 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<ArgsSpecial>例子也可以用EventHandler来写。

请参阅这里了解有关EventHandler的文档

这是一个旧的post,但如果任何人绊倒在上面,就像我做的 – 这是另一个很好的链接引用.. http://csharpindepth.com/Articles/Chapter2/Events.aspx

简要地说,拿走文章 – 事件封装在代表。 从文章引用 –

“假设事件在C#/。NET中不是作为一个概念存在的,另一个类如何订阅一个事件?

三个选项:

  1. 公共委托variables

  2. 由属性支持的委托variables

  3. 使用AddXXXHandler和RemoveXXXHandler方法委托variables

scheme1显然是可怕的,因为我们憎恶公共变数的所有正常原因。

选项2更好,但是允许用户有效地覆盖对方 – 编写someInstance.MyEvent = eventHandler将会非常容易; 这将取代任何现有的事件处理程序,而不是添加新的事件处理程序。 另外,你还需要编写属性。

选项3基本上是事件给你的,但是有一个保证约定(由编译器生成并由IL中的额外标志支持)和一个“免费”实现,如果你对现场类事件给你的语义满意的话。 订阅和取消订阅事件是封装的,不允许任意访问事件处理程序的列表,语言可以通过为声明和订阅提供语法来简化操作。“

你也可以在接口声明中使用事件,而不是委托。

注意:如果您有权访问C#5.0 Unleashed ,请阅读第18章“简单使用代表的限制”,以更好地理解两者之间的差异。


它总是帮助我有一个简单,具体的例子。 所以这里是一个社区。 首先,我将展示如何单独使用代表来完成事件为我们做的事情。 然后,我将展示相同的解决scheme如何与EventHandler的实例一起工作。 然后我解释了为什么我们不想做我在第一个例子中解释的内容。 这篇文章的灵感来自John Skeet 的一篇文章 。

示例1:使用公共代理

假设我有一个WinForms应用程序与一个下拉框。 下拉List<Person>绑定到一个List<Person> 。 其中Person具有Id,Name,NickName,HairColor的属性。 在主窗体上是一个显示该人的属性的自定义用户控件。 当某人在下拉列表中select一个人时,用户控件中的标签将更新以显示所选人员的属性。

在这里输入图像描述

这是如何工作。 我们有三个文件帮助我们把它们放在一起:

  • Mediator.cs – 静态类包含委托
  • Form1.cs – 主窗体
  • DetailView.cs – 用户控件显示所有细节

以下是每个类的相关代码:

 class Mediator { public delegate void PersonChangedDelegate(Person p); //delegate type definition public static PersonChangedDelegate PersonChangedDel; //delegate instance. Detail view will "subscribe" to this. public static void OnPersonChanged(Person p) //Form1 will call this when the drop-down changes. { if (PersonChangedDel != null) { PersonChangedDel(p); } } } 

这是我们的用户控制:

 public partial class DetailView : UserControl { public DetailView() { InitializeComponent(); Mediator.PersonChangedDel += DetailView_PersonChanged; } void DetailView_PersonChanged(Person p) { BindData(p); } public void BindData(Person p) { lblPersonHairColor.Text = p.HairColor; lblPersonId.Text = p.IdPerson.ToString(); lblPersonName.Text = p.Name; lblPersonNickName.Text = p.NickName; } } 

最后我们在Form1.cs中有下面的代码。 在这里我们调用OnPersonChanged,它调用订阅到代理的任何代码。

 private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { Mediator.OnPersonChanged((Person)comboBox1.SelectedItem); //Call the mediator's OnPersonChanged method. This will in turn call all the methods assigned (ie subscribed to) to the delegate -- in this case `DetailView_PersonChanged`. } 

好。 所以这就是你如何得到这个工作, 而不使用事件只使用委托 。 我们只是把一个公共委托放到一个类中 – 你可以把它变成静态的或者单例的,或者其他的东西。 大。

但是,但是,我们不想做我刚才所描述的。 因为公共领域不好 ,原因很多。 那么我们有什么select? 正如John Skeet所描述的,这里是我们的select:

  1. 一个公共的委托variables(这是我们刚刚做的,不要这样做,我刚刚告诉过你为什么不好)
  2. 把委托放入一个带有get / set的属性(问题在于订阅者可以互相覆盖 – 所以我们可以订阅一堆方法给委托,然后我们可以不小心说PersonChangedDel = null ,把所有其他的另一个问题是,由于用户可以访问委托,因此可以调用调用列表中的目标 – 我们不希望外部用户有权访问何时提升事件。
  3. 带有AddXXXHandler和RemoveXXXHandler方法的委托variables

这第三个选项本质上是一个事件给我们的。 当我们声明一个EventHandler的时候,它使我们能够访问一个委托 – 不是公开的,而是一个属性,但是我们把这个事件称为一个只添加/删除访问器的事件。

让我们看看同样的程序是什么样子的,但是现在使用一个事件而不是公共委托(我也把我们的中介变成了一个单例):

示例2:使用EventHandler而不是公共委托

中保

 class Mediator { private static readonly Mediator _Instance = new Mediator(); private Mediator() { } public static Mediator GetInstance() { return _Instance; } public event EventHandler<PersonChangedEventArgs> PersonChanged; //this is just a property we expose to add items to the delegate. public void OnPersonChanged(object sender, Person p) { var personChangedDelegate = PersonChanged as EventHandler<PersonChangedEventArgs>; if (personChangedDelegate != null) { personChangedDelegate(sender, new PersonChangedEventArgs() { Person = p }); } } } 

请注意,如果您在EventHandler上使用F12,它将向您显示定义仅仅是一个具有额外“发件人”对象的通用委托代理:

 public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e); 

用户控件:

 public partial class DetailView : UserControl { public DetailView() { InitializeComponent(); Mediator.GetInstance().PersonChanged += DetailView_PersonChanged; } void DetailView_PersonChanged(object sender, PersonChangedEventArgs e) { BindData(e.Person); } public void BindData(Person p) { lblPersonHairColor.Text = p.HairColor; lblPersonId.Text = p.IdPerson.ToString(); lblPersonName.Text = p.Name; lblPersonNickName.Text = p.NickName; } } 

最后,这里是Form1.cs代码:

 private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { Mediator.GetInstance().OnPersonChanged(this, (Person)comboBox1.SelectedItem); } 

因为EventHandler想要和EventArgs作为参数,我创build了这个类只有一个属性:

 class PersonChangedEventArgs { public Person Person { get; set; } } 

希望能够向大家展示为什么我们有事件,以及它们与代表们在function上是如何不同的。

.net中的事件是Add方法和Remove方法的指定组合,两者都期望某种特定types的委托。 C#和vb.net都可以为add和remove方法自动生成代码,这些方法将定义一个委托来保存事件订阅,并向/从该订阅委托中添加/删除传入的委托。 VB.net也会自动生成代码(用RaiseEvent语句)来调用订阅列表,当且仅当非空; 出于某种原因,C#不会生成后者。

请注意,尽pipe使用多播委托来pipe理事件订阅是很常见的,但这不是唯一的方法。 从公共的angular度来看,一个可能的事件订阅者需要知道如何让一个对象知道它想要接收事件,但是并不需要知道发布者将用什么机制来提升事件。 还要注意的是,尽pipe在.net中定义事件数据结构的人显然认为应该有一个提高它们的公共方法,但是C#和vb.net都没有使用这个特性。

事件和代表之间有什么很大的误解! 一个委托指定一个TYPE(比如一个class或者一个interface ),而一个事件只是一种会员(比如字段,属性等)。 而且,就像任何其他types的成员一样,事件也是一种types。 然而,就事件而言,事件的types必须由委托人指定。 例如,你不能声明一个接口定义的types的事件。

最后,我们可以做出以下观察:事件的types必须由委托人定义 。 这是一个事件和一个代表之间的主要关系,并在第II.18节中定义 ECMA-335(CLI)分区I到VI的事件 :

在典型的用法中,TypeSpec(如果存在的话) 标识一个委托,它的签名与传递给事件的fire方法的参数相匹配。

但是, 这个事实并不意味着一个事件使用后台代理字段 。 事实上,一个事件可能会使用您select的任何不同数据结构types的后台字段。 如果你在C#中明确地实现了一个事件,你可以自由select你存储事件处理程序的方式 (注意事件处理程序事件 types的实例,而事件处理程序又是强制委托types的实例) )。 但是,您可以将这些事件处理程序(它们是委托实例)存储在数据结构(如ListDictionary或任何其他数据结构中,甚至可以存储在后台代理字段中。 但是不要忘记,使用委托字段并不是强制性的。

以简单的方式定义事件:

事件是对有两个限制的代表的参考

  1. 无法直接调用
  2. 不能直接赋值(如eventObj = delegateMethod)

以上两点是代表的薄弱环节,并在事件处理。 完整的代码示例,以显示在小提琴手的差异是在这里https://dotnetfiddle.net/5iR3fB

切换事件和委托之间的评论和客户端代码调用/分配值委托来了解差异

这是内联代码。

  /* This is working program in Visual Studio. It is not running in fiddler because of infinite loop in code. This code demonstrates the difference between event and delegate Event is an delegate reference with two restrictions for increased protection 1. Cannot be invoked directly 2. Cannot assign value to delegate reference directly Toggle between Event vs Delegate in the code by commenting/un commenting the relevant lines */ public class RoomTemperatureController { private int _roomTemperature = 25;//Default/Starting room Temperature private bool _isAirConditionTurnedOn = false;//Default AC is Off private bool _isHeatTurnedOn = false;//Default Heat is Off private bool _tempSimulator = false; public delegate void OnRoomTemperatureChange(int roomTemperature); //OnRoomTemperatureChange is a type of Delegate (Check next line for proof) // public OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), public event OnRoomTemperatureChange WhenRoomTemperatureChange;// { get; set; }//Exposing the delegate to outside world, cannot directly expose the delegate (line above), public RoomTemperatureController() { WhenRoomTemperatureChange += InternalRoomTemperatuerHandler; } private void InternalRoomTemperatuerHandler(int roomTemp) { System.Console.WriteLine("Internal Room Temperature Handler - Mandatory to handle/ Should not be removed by external consumer of ths class: Note, if it is delegate this can be removed, if event cannot be removed"); } //User cannot directly asign values to delegate (eg roomTempControllerObj.OnRoomTemperatureChange = delegateMethod (System will throw error) public bool TurnRoomTeperatureSimulator { set { _tempSimulator = value; if (value) { SimulateRoomTemperature(); //Turn on Simulator } } get { return _tempSimulator; } } public void TurnAirCondition(bool val) { _isAirConditionTurnedOn = val; _isHeatTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary) System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn); System.Console.WriteLine("Heat :" + _isHeatTurnedOn); } public void TurnHeat(bool val) { _isHeatTurnedOn = val; _isAirConditionTurnedOn = !val;//Binary switch If Heat is ON - AC will turned off automatically (binary) System.Console.WriteLine("Aircondition :" + _isAirConditionTurnedOn); System.Console.WriteLine("Heat :" + _isHeatTurnedOn); } public async void SimulateRoomTemperature() { while (_tempSimulator) { if (_isAirConditionTurnedOn) _roomTemperature--;//Decrease Room Temperature if AC is turned On if (_isHeatTurnedOn) _roomTemperature++;//Decrease Room Temperature if AC is turned On System.Console.WriteLine("Temperature :" + _roomTemperature); if (WhenRoomTemperatureChange != null) WhenRoomTemperatureChange(_roomTemperature); System.Threading.Thread.Sleep(500);//Every second Temperature changes based on AC/Heat Status } } } public class MySweetHome { RoomTemperatureController roomController = null; public MySweetHome() { roomController = new RoomTemperatureController(); roomController.WhenRoomTemperatureChange += TurnHeatOrACBasedOnTemp; //roomController.WhenRoomTemperatureChange = null; //Setting NULL to delegate reference is possible where as for Event it is not possible. //roomController.WhenRoomTemperatureChange.DynamicInvoke();//Dynamic Invoke is possible for Delgate and not possible with Event roomController.SimulateRoomTemperature(); System.Threading.Thread.Sleep(5000); roomController.TurnAirCondition (true); roomController.TurnRoomTeperatureSimulator = true; } public void TurnHeatOrACBasedOnTemp(int temp) { if (temp >= 30) roomController.TurnAirCondition(true); if (temp <= 15) roomController.TurnHeat(true); } public static void Main(string []args) { MySweetHome home = new MySweetHome(); } }