除了语法糖之外,我并没有看到使用事件比委托的优势。也许我有误解,但似乎事件只是委托的占位符。
您能否向我解释一下它们之间的区别以及何时使用哪个?它们各自的优缺点是什么?我们的代码与事件密切相关,我想彻底了解它们。
在什么情况下会使用委托,何时使用事件?请说明您在实际生产代码中对两者的经验。
除了语法糖之外,我并没有看到使用事件比委托的优势。也许我有误解,但似乎事件只是委托的占位符。
您能否向我解释一下它们之间的区别以及何时使用哪个?它们各自的优缺点是什么?我们的代码与事件密切相关,我想彻底了解它们。
在什么情况下会使用委托,何时使用事件?请说明您在实际生产代码中对两者的经验。
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方法在语法上更加显眼(有点生硬)。
从技术角度来看,其他回答已经解答了它们的不同之处。
从语义学的角度来看,事件是当对象达到某些条件时引发的操作。例如,我的股票类有一个名为Limit的属性,当股票价格达到限制时会引发一个事件。这种通知是通过事件完成的。无论是否有人真正关心这个事件并订阅它,都超出了所有者类的关注范围。
委托(delegate)是一个更通用的术语,用于描述类似于C/C ++术语中指针的构造。.Net中的所有委托都是多路广播委托。从语义上讲,它们通常被用作一种输入方式。特别地,它们是实现策略模式的完美方法。例如,如果我想对对象列表进行排序,可以向该方法提供比较器策略以告诉实现如何比较两个对象。
我使用过这两种方法在生产代码中。我的大量数据对象都会在满足某些属性时通知。最基本的示例是,每当属性更改时,都会引发PropertyChanged事件(请参见INotifyPropertyChanged接口)。我还在代码中使用委托来提供将某些对象转换为字符串的不同策略。这个特殊的例子是一个将某个对象类型的ToString()方法实现列表显式给用户的示例。
事件是一种语法糖。它们非常好吃。当我看到一个事件时,我知道该怎么做。但当我看到委托时,就不太确定了。
将事件与接口(更多的糖)结合起来会制作出令人垂涎欲滴的小吃。而委托和纯虚拟抽象类则不那么可口。
在元数据中,事件被标记为事件。这使得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
类的类都可以通过调用animal.RaiseEvent()
来引发RaiseEvent
。
为避免这些弱点,您可以在C#中使用事件
。
您的Animal类将以此方式更改
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)
它需要一个发送者(Object类型)和事件参数。如果它来自静态方法,则发送器为null。
您也可以使用EventHAndler
,而不是此示例中使用的EventHandler<ArgsSpecial>
请参见此处有关EventHandler的文档
编辑#1 您何时会使用委托而不是事件?请说明您在实际生产代码中对两者的经验。
当我设计自己的API时,我定义了委托,并将其作为参数传递给方法或类的构造函数:
Predicate
和Action
委托被传递给.Net通用集合类)这些委托通常在运行时是必需的(即不能为null
)。
我倾向于不使用事件;但是在我使用事件的情况下,我将其用于可选地向零个、一个或多个客户端发出信号,这些客户端可能感兴趣,即当一个类存在并运行时,是否有任何客户端添加了事件处理程序到其事件上(例如,窗体的“鼠标按下”事件存在,但是是否有外部客户端有兴趣安装事件处理程序到该事件上是可选的)。
public class ConsoleLog
{
public ConsoleLog(Operation operation)
{
operation.EventLogHandler += print;
}
public void print(string str)
{
Console.WriteLine("write on console : " + str);
}
}
类通过订阅日志事件。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");
}
}