C# 事件和委托的区别及其各自的应用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/563549/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 08:19:37  来源:igfitidea点击:

Difference between events and delegates and its respective applications

c#eventsdelegates

提问by

I don't see advantages of using events over delegates, other than being syntactical sugar. Perhaps I am misunderstanding, but it seems that event is just a placeholder for delegate.

除了语法糖之外,我没有看到使用事件优于委托的优势。也许我误解了,但似乎事件只是代表的占位符。

Would you explain to me the differences and when to use which? What are the advantages and disadvantages? Our code is heavily rooted with events, and I want to get to the bottom of it.

您能向我解释一下这些区别以及何时使用哪个吗?有什么优点和缺点?我们的代码深深植根于事件,我想深入了解它。

When would you use delegates over events and vice versa? Please state your real world experience with both, say in the production code.

你什么时候会在事件上使用委托,反之亦然?请在生产代码中说明您对两者的真实体验。

采纳答案by Szymon Rozga

From the technical standpoint, other answers have addressed the differences.

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

From a semantics perspective, events are actions raised by an object when certain conditions are met. For example, my Stock class has a property called Limit, and it raises an event when the stock prices reaches the Limit. This notification is done via an event. Whether anyone actually cares about this event and subscribes to it is beyond the concern of the owner class.

从语义的角度来看,事件是对象在满足某些条件时引发的动作。例如,我的 Stock 类有一个名为 Limit 的属性,它会在股票价格达到 Limit 时引发一个事件。此通知是通过事件完成的。是否有人真正关心这个事件并订阅它超出了所有者类的关注。

A delegate is a more generic term to describe a construct similar to a pointer in C/C++ terms. All delegates in .Net are multicast delegates. From a semantics perspective, they are generally used as a kind of input. In particular, they are a perfect way to implement the Strategy Pattern. For example, if I want to sort a List of objects, I can provide a Comparator strategy to the method to tell the implementation how to compare two objects.

委托是一个更通用的术语,用于描述类似于 C/C++ 术语中的指针的构造。.Net 中的所有委托都是多播委托。从语义的角度来看,它们通常用作一种输入。特别是,它们是实现Strategy Pattern的完美方式。例如,如果我想对一个 List 的对象进行排序,我可以为该方法提供一个 Comparator 策略来告诉实现如何比较两个对象。

I have used the two methods in production code. Tons of my data objects notify when certain properties are met. Most basic example, whenever a property changes, a PropertyChanged event is raised (see INotifyPropertyChanged interface). I have used delegates in code to provide different strategies of turning certain objects into string. This particular example was a glorified ToString() list of implementations for a particular object type to display it to users.

我在生产代码中使用了这两种方法。当满足某些属性时,我的大量数据对象会通知。最基本的示例,每当属性更改时,都会引发 PropertyChanged 事件(请参阅 INotifyPropertyChanged 接口)。我在代码中使用了委托来提供将某些对象转换为字符串的不同策略。这个特殊的例子是一个美化的 ToString() 实现列表,用于特定对象类型以向用户显示它。

回答by Sean

Events are syntactical sugar. They are delicious. When I see an event, I know what to do. When I see a delegate, I'm not so sure.

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

Combining events with interfaces (more sugar) makes for a mouth watering snack. Delegates and pure virtual abstract classes are much less appetizing.

将事件与接口(更多的糖)相结合,是一种令人垂涎的小吃。委托和纯虚拟抽象类的吸引力要小得多。

回答by itowlson

Events are marked as such in the metadata. This allows things like the Windows Forms or ASP.NET designers to distinguish events from mere properties of delegate type, and provide appropriate support for them (specifically showing them on the Events tab of the Properties window).

事件在元数据中被标记为这样。这允许诸如 Windows 窗体或 ASP.NET 设计器之类的东西将事件与仅委托类型的属性区分开来,并为它们提供适当的支持(特别是在“属性”窗口的“事件”选项卡上显示它们)。

Another difference from a property of delegate type is that users can only add and remove event handlers, whereas with a property of delegate type they can set the value:

与委托类型的属性的另一个区别是用户只能添加和删除事件处理程序,而对于委托类型的属性,他们可以设置值:

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

This helps to isolate event subscribers: I can add my handler to an event, and you can add your handler to the same event, and you won't accidentally overwrite my handler.

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

回答by Peter Wone

The keyword eventis a scope modifier for multicast delegates. Practical differences between this and just declaring a multicast delegate are as follows:

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

  • You can use eventin an interface.
  • Invocation access to the multicast delegate is limited to the declaring class. The behaviour is as though the delegate were private for invocation. For the purposes of assignment, access is as specified by an explicit access modifier (eg public event).
  • 您可以event在界面中使用。
  • 对多播委托的调用访问仅限于声明类。该行为就好像委托是私有的以供调用。出于赋值的目的,访问由显式访问修饰符(例如public event)指定。

As a matter of interest, you can apply +and -to multicast delegates, and this is the basis of the +=and -=syntax for the combination assignment of delegates to events. These three snippets are equivalent:

由于利益的问题,你可以申请+-多播的代表,这是对的基础+=-=语法为代表的组合分配给事件。这三个片段是等效的:

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

Sample two, illustrating both direct assignment and combination assignment.

示例二,说明直接分配和组合分配。

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

Sample three: more familiar syntax. You are probably acquainted with the assignment of null to remove all handlers.

示例三:更熟悉的语法。您可能熟悉分配 null 以删除所有处理程序。

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

Like properties, events have a full syntax that no-one ever uses. This:

与属性一样,事件也有一种从未有人使用过的完整语法。这个:

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;
    }
  }

  ...
}

...does exactlythe same as this:

...与此完全相同

class myExample 
{
  public event EventHandler OnSubmit;
}

The add and remove methods are more conspicuous in the rather stilted syntax that VB.NET uses (no operator overloads).

add 和 remove 方法在 VB.NET 使用的相当生硬的语法中更为显眼(没有运算符重载)。

回答by Robert Gould

Although I've got no technical reasons for it, I use events in UI style code, in other words, in the higher levels of the code, and use delegates for logic that lays deeper in the code. As I say you could use either, but I find this use pattern to be logically sound, if nothing else, it helps document the types of callbacks and their hierarchy too.

虽然我没有技术上的原因,但我在 UI 样式代码中使用事件,换句话说,在代码的更高级别,并使用委托处理代码中更深层次的逻辑。正如我所说,您可以使用任何一种,但我发现这种使用模式在逻辑上是合理的,如果不出意外,它也有助于记录回调的类型及其层次结构。



Edit: I think the difference in usage patterns I have would be that, I find it perfectly acceptable to ignore events, they are hooks/stubs, if you need to know about the event, listen to them, if you don't care about the event just ignore it. That's why I use them for UI, kindof Javascript/Browser event style. However when I have a delegate, I expect REALLY expect someone to handle the delegate's task, and throw an exception if not handled.

编辑:我认为我的使用模式的不同之处在于,我发现忽略事件是完全可以接受的,它们是钩子/存根,如果您需要了解事件,请听听它们,如果您不关心该事件只是忽略它。这就是为什么我将它们用于 UI,某种 Javascript/浏览器事件样式。但是,当我有委托时,我真的希望有人来处理委托的任务,如果没有处理就抛出异常。

回答by ChrisW

Edit#1When would you use delegates over events and vs.versa? Please state your real world experience with both, say in the production code.

编辑#1你什么时候会在事件和 vs.versa 上使用委托?请在生产代码中说明您对两者的真实体验。

When I design my own APIs, I define delegates which are passed as parameters to methods, or to the constructors of classes:

当我设计自己的 API 时,我定义了作为参数传递给方法或类的构造函数的委托:

  • So that a method can implement a simple 'template method' pattern (as e.g. the Predicateand Actiondelegates are passed to the .Net generic collection classes)
  • Or so that the class can do a 'callback' (typically a callback to a method of the class which created it).
  • 这样一个方法就可以实现一个简单的“模板方法”模式(例如,将PredicateAction委托传递给 .Net 泛型集合类)
  • 或者使类可以执行“回调”(通常是对创建它的类的方法的回调)。

These delegates are generally non-optionalat run-time (i.e. mustn't be null).

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

I tend not to use events; but where I do use events, I use them for optionallysignalling events to zero, one, or moreclients that mightbe interested, i.e. when it makes sense that a class (e.g. the System.Windows.Formclass) should exist and run whether or not any client has added an event handler to its event (e.g. the form's 'mouse down' event exists, but it's optionalwhether any external client is interested in installing an event handler onto that event).

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

回答by supercat

Although events are typically implemented with multicast delegates, there is no requirement that they be used in such fashion. If a class exposes event, that means the class exposes two methods. Their meanings are, in essence:

尽管事件通常使用多播委托来实现,但并不要求以这种方式使用它们。如果一个类公开事件,那意味着该类公开了两个方法。它们的含义本质上是:

  1. Here's a delegate. Please invoke it when something interesting happens.
  2. Here's a delegate. You should destroy all reference to it as soon as convenient (and no longer call it).
  1. 这是一个代表。请在发生有趣的事情时调用它。
  2. 这是一个代表。您应该尽快销毁对它的所有引用(并且不再调用它)。

The most common way for a class to handle an event it exposes is to define a multicast delegate, and add/remove any delegates that get passed to the above methods but there is no requirement that they work that way. Unfortunately, the event architecture fails to do some things that would have made alternative approaches much cleaner (e.g. have the subscription method return a MethodInvoker, which would be kept by the subscriber; to unsubscribe an event, simply invoke the returned method) so multicast delegates are by far the most common approach.

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

回答by faby

to understand the differences you can look at this 2 examples

要了解差异,您可以查看这两个示例

Exemple with Delegates (Action in this case that is a kind of delegate that doen't return value)

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

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

to use the delegate you should do something like this

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

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

this code works well but you could have some weak spots.

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

For example if I write this

例如,如果我写这个

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

with the last line of code I had override the previous behaviors just with one missing +(I have used +instead of +=)

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

Another weak spot is that every class that use your Animalclass can raise RaiseEventjust calling it animal.RaiseEvent().

另一个弱点是每个使用你的Animal类的类都可以通过RaiseEvent调用它来引发animal.RaiseEvent()

To avoid this weak spots you can use eventsin c#.

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

Your Animal class will change in this way

你的 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"));
       }
    }

to call events

调用事件

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

Differences:

区别:

  1. You aren't using a public property but a public field (with events the compiler protect your fields from unwanted access)
  2. Events can't directly be assigned. In this case you can't do the previous error that I have showed with overriding the behavior.
  3. No one outside of your class can raise the event.
  4. Events can be included in an interface declaration, whereas a field cannot
  1. 您使用的不是公共属性,而是公共字段(通过事件,编译器会保护您的字段免受不必要的访问)
  2. 不能直接分配事件。在这种情况下,您无法执行我通过覆盖行为显示的先前错误。
  3. 班级以外的任何人都不能引发事件。
  4. 事件可以包含在接口声明中,而字段不能

notes

笔记

EventHandler is declared as the following delegate:

EventHandler 被声明为以下委托:

public delegate void EventHandler (object sender, EventArgs e)

it takes a sender (of Object type) and event arguments. The sender is null if it comes from static methods.

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

You can use also EventHAndlerinstead this example that use EventHandler<ArgsSpecial>

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

refer herefor documentation about EventHandler

有关 EventHandler 的文档,请参阅此处

回答by Pontus Wittenmark

The difference between events and delegates is a lot smaller than I used to think.. I just posted a super short YouTube video on the subject: https://www.youtube.com/watch?v=el-kKK-7SBU

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

Hope this helps!

希望这可以帮助!

回答by Narottam Goyal

If we use only delegate in place of Event then subscriber has the opportunity to clone(), invoke() the delegate itself as shown below in the image. Which is not right.

如果我们只使用委托代替事件,那么订阅者有机会克隆(),调用()委托本身,如下图所示。这是不对的。

enter image description here

在此处输入图片说明

That's the main difference b/w event and delegate. the subscriber has only one right i.e. listening to the events

ConsoleLog class is subscribing log events via EventLogHandler

这是黑白事件和委托的主要区别。订阅者只有一项权利,即监听事件

ConsoleLog 类通过 EventLogHandler 订阅日志事件

public class ConsoleLog
{
    public ConsoleLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write on console : " + str);
    }
}

FileLog class is subscribing log events via EventLogHandler

FileLog 类通过 EventLogHandler 订阅日志事件

public class FileLog
{
    public FileLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write in File : " + str);
    }
}

Operation class is publishing log events

操作类正在发布日志事件

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");
    }
}