什么是委托和事件之间的区别是什么? 不要都认为可以执行到函数的引用?
Answer 1:
事件声明增加了抽象和保护的委托实例层。 这种保护可以防止委托的客户从重置代理和它的调用列表,并只允许添加或从调用列表中移除目标。
Answer 2:
要了解这两个例子,你可以看看的差异
与代理实例(在这种情况下,一个动作 - 这是一种代表的是不返回值)
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()
为了避免这些弱点,你可以使用events
在C#。
你的动物类会以这种方式改变:
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();
区别:
- 您使用的不是公共财产,但公共领域(使用事件,编译器保护您的域免受不必要的访问)
- 事件不能直接分配。 在这种情况下,也不会产生以前的错误,我已经与重写的行为表现。
- 没有一个类以外可以引发事件。
- 事件可以被包含在一个接口声明,而字段不能
笔记:
事件处理程序被宣布为以下代表:
public delegate void EventHandler (object sender, EventArgs e)
它需要和事件参数(对象类型的)发送器。 发件人为空,如果它来自静态方法。
本例中,它使用EventHandler<ArgsSpecial>
也可以使用被写入EventHandler
代替。
请参阅这里有关事件处理的文档
Answer 3:
除了语法和操作特性,还有一个语义差异。
代表们,概念,功能模板; 也就是说,他们表达的功能,必须坚持以合同被认为是委托的“类型”的。
事件代表......嗯,事件。 他们的目的是有事时提醒别人,是的,他们坚持一个委托的定义,但它们不是一回事。
即使他们是完全一样的东西(语法,并在IL代码)仍然会留在语义差别。 总的来说,我喜欢有两个不同的概念两个不同的名字,即使他们以同样的方式实现(这并不意味着我喜欢有相同的代码两次)。
Answer 4:
这里是另一个很好的链接来指代。 http://csharpindepth.com/Articles/Chapter2/Events.aspx
简单地说,采取从文章中了 - 事件是封装了委托。
从文章引述如下:
假设事件是不存在的,如C#/。NET的概念。 另一个类将如何订阅事件? 三个选项:
公共委托变量
由房地产支持的委托变量
与AddXXXHandler和RemoveXXXHandler方法的代表可变
选项1显然是太可怕了,所有我们憎恶公共变量正常的原因。
选项2是稍微好一点,但允许用户有效地互相覆盖 - 这将是太容易写someInstance.MyEvent =事件处理程序; 这将取代现有的事件处理程序,而不是添加一个新的。 此外,你还需要编写的属性。
选项3基本上是什么事件,给你,但是有保证约定(由编译器生成,并在IL通过额外的标志支持)和“自由”的实现,如果你觉得满意了现场般的事件给你的语义。 订阅和退订事件封装,而不允许将事件处理程序的列表中随机存取和语言可以使事情由两个宣言和订阅提供语法简单。
Answer 5:
注意:如果你有机会到C#5.0偷跑 ,在标题为“事件”,以更好地了解两者之间的差异第18章宣读了“关于使用普通的代表限制”。
它总是帮助我有一个简单,具体的例子。 因此,这里是一个社区。 首先,我向您展示如何使用委托做单为我们做什么活动。 然后,我将展示如何相同的解决方案将与实例工作的EventHandler
。 然后,我解释了为什么我们不想做什么,我在第一个例子来说明。 这篇文章的灵感来自于一篇文章约翰飞碟双向。
实施例1:使用公共委托
假设我有一个下拉框WinForms应用程序。 下拉绑定到一个List<Person>
。 当人有身份证,姓名,昵称,染发的性能。 上的主要形式是一个定制的用户控制,显示该人的属性。 当有人在下拉列表中选择一个人在用户控制更新的标签,以显示所选择的人的属性。
这里是如何工作的。 我们有三个文件,帮助我们把这个一起:
- 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 (i.e. subscribed to) to the delegate -- in this case `DetailView_PersonChanged`.
}
好。 这就是你如何得到这个工作,而无需使用事件 , 只是使用委托 。 我们只是把公众的委托为一类 - 你可以把它的静态或单,或什么的。 大。
但是,但是,但是,我们不想做什么,我只是如上所述。 因为公共领域是不好的很多,很多原因。 那么,什么是我们的选择? 正如约翰·斯基特介绍,这是我们的选择:
- 公共委托变量(这是我们正上方做。不这样做。我只是告诉你在上面为什么它是坏的)
- 将委托与一对get / set属性(这里的问题是,用户可以覆盖对方-这样我们就可以订阅了一堆的方法来委托,然后我们可能会不小心说
PersonChangedDel = null
,消灭所有其他的订阅剩下在这里的是,既然用户可以访问的委托,就可以调用调用列表中的目标的另一个问题 - 我们不希望有机会获得在提高我们活动的外部用户。 - 与AddXXXHandler和RemoveXXXHandler方法的代表可变
这第三个选项是基本上就是一个事件给我们。 当我们声明的事件处理程序,它给了我们访问的委托 - 不公开,而不是作为一个属性,但因为这件事,我们称之为刚刚添加/删除访问者的事件。
让我们来看看同一个程序看起来像什么,但现在用公众委托的事件,而不是(我也改变了我们的中保到一个单):
实施例2:利用事件处理程序,而不是一个公共委托
中保
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 });
}
}
}
请注意,如果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);
}
由于事件处理程序要和的EventArgs作为参数,我创造了这个类在它只是一个单一的属性:
class PersonChangedEventArgs
{
public Person Person { get; set; }
}
希望这显示了一点关于为什么我们的活动以及它们是不同的 - 但功能相同的 - 作为代表。
Answer 6:
您还可以使用事件接口声明,不那么为代表。
Answer 7:
真是一个伟大的误解事件和委托之间! 一个代表指定一个TYPE(诸如class
或interface
一样),而事件只是一种构件(诸如字段,属性等)。 而且,就像任何其他种类的成员的事件也有类型。 然而,在一个事件的情况下,事件的类型必须由委托指定。 举例来说,你不能声明由接口定义的类型的事件。
最后,我们可以做出以下观察:事件的类型必须由委托定义 。 这是一个事件和委托之间的主要关系,并在部分中描述II.18定义的事件 ECMA-335(CLI)分区I至VI :
在典型的用法中,类型指定(如果存在) 标识代表的签名传递给事件的触发方法的参数匹配。
然而, 这一事实并不意味着事件使用后盾委托场 。 事实上,一个事件可以使用任何不同的数据结构类型您所选择的支持字段。 如果你在C#中明确实现的情况下,你可以自由选择你存储的事件处理程序 (注意, 事件处理程序 的事件的类型 ,这又是强制委托类型 ---从以前的观察实例的方式)。 但是,你可以存储这些事件处理程序(这是委托实例)的数据结构,如List
或Dictionary
或其他任何东西,甚至在背委托场。 但不要忘了,这是不是强制性的,你使用委托场。
Answer 8:
在.NET事件是一个Add方法和删除方法,这两者都期待一些特定类型的委托的指定组合。 C#和vb.net可以自动生成的代码,添加和删除,这将定义委托头筹订阅方法,并添加/从订阅委托删除传入delegagte到/。 VB.net也将自动生成代码(与RaiseEvent语句)来调用订阅列表,当且仅当它不为空; 出于某种原因,C#不生成后者。
请注意,虽然是常见的管理使用多播委托事件订阅,是不是这样做的唯一手段。 从公众的角度来看,一个想成为事件的用户需要知道如何让对象知道它要接收的事件,但它并不需要知道出版商会用什么机制来提高的事件。 还要注意的是,虽然凡在.NET中定义的事件数据结构显然认为应该有养育孩子的一个公共交通工具,无论是C#也不vb.net利用该功能。
Answer 9:
要定义有关在简单的方法事件:
事件是参考了委托有两个限制
- 不能直接调用
- 不能直接分配的值(例如是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 (e.g. 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();
}
}
Answer 10:
委托是类型安全的函数指针。 事件是使用代表发布者 - 订阅设计模式的实现。
Answer 11:
Covariance
和Contravariance
提供额外的灵活性,以委托对象。 在另一方面,事件还没有这样的概念。
-
Covariance
允许你的方法分配给代表其中该方法的返回类型是从指定委托的返回类型的类派生的类。 -
Contravariance
允许你的方法分配给代表其中参数类型的方法的是一个基类被指定为委托的参数之类的。