除了语法上的糖以外,我没有看到使用事件优于委托的优势。也许我误会了,但似乎该事件只是代表的占位符。
您能给我解释一下区别和何时使用吗?优点和缺点是什么?我们的代码深深植根于事件,我想深入了解它。
什么时候在事件上使用委托,反之亦然?请在生产代码中说明您在这两个方面的实际经验。
除了语法上的糖以外,我没有看到使用事件优于委托的优势。也许我误会了,但似乎该事件只是代表的占位符。
您能给我解释一下区别和何时使用吗?优点和缺点是什么?我们的代码深深植根于事件,我想深入了解它。
什么时候在事件上使用委托,反之亦然?请在生产代码中说明您在这两个方面的实际经验。
Answers:
从技术角度来看,其他答案也解决了这些差异。
从语义的角度来看,事件是指满足特定条件时对象引发的动作。例如,我的Stock类具有一个名为Limit的属性,当股票价格达到Limit时会引发一个事件。该通知是通过事件完成的。是否有人真正在乎并订阅该事件,这超出了所有者类的关注范围。
委托是一个更通用的术语,用于描述类似于C / C ++术语中的指针的构造。.Net中的所有委托都是多播委托。从语义的角度来看,它们通常被用作一种输入。特别是,它们是实施策略模式的理想方法。例如,如果要对对象列表进行排序,则可以向该方法提供比较器策略,以告诉实现如何比较两个对象。
我在生产代码中使用了两种方法。当满足某些属性时,我的数据对象会发出通知。最基本的示例,每当属性更改时,都会引发PropertyChanged事件(请参见INotifyPropertyChanged接口)。我在代码中使用委托来提供将某些对象转换为字符串的不同策略。这个特定的示例是针对特定对象类型的美丽的ToString()实现列表,以将其显示给用户。
关键字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使用的语法非常呆滞的情况下(没有运算符重载),add和remove方法更为明显。
事件是句法糖。它们很美味。当我看到一个事件时,我知道该怎么办。当我看到代表时,我不太确定。
将事件与界面(更多的糖)结合起来,可以制成令人垂涎的小吃。委托和纯虚拟抽象类的吸引力不大。
事件在元数据中被标记为此类。这使诸如Windows Forms或ASP.NET设计器之类的事件能够将事件与仅代表类型的属性区分开来,并为它们提供适当的支持(特别是在“属性”窗口的“事件”选项卡上显示它们)。
与委托类型的属性的另一个区别是,用户只能添加和删除事件处理程序,而对于委托类型的属性,用户可以设置值:
someObj.SomeCallback = MyCallback; // okay, replaces any existing callback
someObj.SomeEvent = MyHandler; // not okay, must use += instead
这有助于隔离事件订阅者:我可以将处理程序添加到事件中,并且可以将处理程序添加到同一事件中,并且不会意外覆盖我的处理程序。
要了解差异,您可以看一下这两个示例
带委托的示例(在这种情况下,动作是一种不返回值的委托)
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()
。
为了避免这种弱点,您可以events
在c#中使用。
您的动物类别将以这种方式改变
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();
差异:
笔记
EventHandler被声明为以下委托:
public delegate void EventHandler (object sender, EventArgs e)
它需要一个(对象类型的)发送者和事件参数。如果发件人来自静态方法,则它为null。
您也可以使用EventHAndler
此示例代替EventHandler<ArgsSpecial>
请参阅此处以获取有关EventHandler的文档
编辑#1 什么时候在事件和vs.versa上使用委托?请在生产代码中说明这两种情况的实际使用经验。
在设计自己的API时,我定义了作为参数传递给方法或类的构造函数的委托:
Predicate
和Action
委托传递给.Net通用集合类)这些委托在运行时通常是非可选的(即不能为null
)。
我倾向于不使用事件。但是在使用事件的地方,我可以选择使用它们将事件发信号通知给可能感兴趣的零个,一个或多个客户端,即,当有意义的一个类(例如该类)应该存在并运行时,是否有任何客户端具有在其事件中添加了一个事件处理程序(例如,表单的'mouse down'事件存在,但是是否有外部客户端有兴趣在该事件上安装事件处理程序是可选的)。System.Windows.Form
尽管没有技术上的原因,但是我在UI样式代码中使用事件,换句话说,在代码的更高级别中使用事件,并使用委托来表示代码中更深层的逻辑。正如我说的那样,您可以使用其中任何一种,但是我发现这种使用模式在逻辑上是合理的,如果没有其他用途,它也有助于记录回调的类型及其层次结构。
编辑:我认为使用模式的不同之处在于,我发现忽略事件是完全可以接受的,它们是钩子/存根,如果您需要了解事件,请听它们,如果您不在乎该事件只是忽略它。这就是为什么我将它们用于UI,一种Javascript / Browser事件样式。但是,当我有一个委托时,我真的希望有人处理委托的任务,如果没有处理,则会引发异常。
活动和代表之间的区别比我以前想象的要小得多。.我刚刚在主题上发布了一个超短的YouTube视频:https : //www.youtube.com/watch?v=el-kKK-7SBU
希望这可以帮助!
如果我们仅使用委托代替Event,则订阅者将有机会克隆(),调用()委托本身,如下图所示。这是不对的。
那是黑白事件和委托的主要区别。订户只有一项权利,即听事件
ConsoleLog类通过EventLogHandler订阅日志事件
public class ConsoleLog
{
public ConsoleLog(Operation operation)
{
operation.EventLogHandler += print;
}
public void print(string str)
{
Console.WriteLine("write on console : " + str);
}
}
FileLog类通过EventLogHandler订阅日志事件
public class FileLog
{
public FileLog(Operation operation)
{
operation.EventLogHandler += print;
}
public void print(string str)
{
Console.WriteLine("write in File : " + str);
}
}
操作类正在发布日志事件
public delegate void logDelegate(string str);
public class Operation
{
public event logDelegate EventLogHandler;
public Operation()
{
new FileLog(this);
new ConsoleLog(this);
}
public void DoWork()
{
EventLogHandler.Invoke("somthing is working");
}
}