代表和活动之间有什么区别?

发布于 2024-08-02 08:08:55 字数 39 浏览 6 评论 0 原文

代表和活动之间有什么区别?两者不都包含对可以执行的函数的引用吗?

What are the differences between delegates and an events? Don't both hold references to functions that can be executed?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

温柔一刀 2024-08-09 08:08:55

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

An Event declaration adds a layer of abstraction and protection on the delegate instance. This protection prevents clients of the delegate from resetting the delegate and its invocation list and only allows adding or removing targets from the invocation list.

℡Ms空城旧梦 2024-08-09 08:08:55

要了解差异,您可以查看这 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 类的类都可以直接调用委托。例如,animal.Run()animal.Run.Invoke() 在 Animal 类之外有效。

为了避免这些弱点,您可以在 C# 中使用事件

您的 Animal 类将以这种方式更改:

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. 您班级之外的任何人都无法引发或调用该事件。例如,animal.Run()animal.Run.Invoke() 在 Animal 类之外无效,并且会产生编译器错误。
  4. 事件可以包含在接口声明中,而字段则不能。

注意:

EventHandler 被声明为以下委托:

public delegate void EventHandler (object sender, EventArgs e)

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

此示例使用 EventHandler,也可以使用 EventHandler 编写。

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

To understand the differences you can look at this 2 examples

Example with Delegates (in this case, an Action - that is a kind of delegate that doesn't return a value)

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:

Animal 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 have overridden the previous behaviors just with one missing + (I have used = instead of +=)

Another weak spot is that every class which uses your Animal class can invoke the delegate directly. For example, animal.Run() or animal.Run.Invoke() are valid outside the Animal class.

To avoid these weak spots you can use events in c#.

Your Animal class will change in this way:

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

to call events

 Animal 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 (using events, the compiler protects your fields from unwanted access)
  2. Events can't be assigned directly. In this case, it won't give rise to the previous error that I have showed with overriding the behavior.
  3. No one outside of your class can raise or invoke the event. For example, animal.Run() or animal.Run.Invoke() are invalid outside the Animal class and will produce compiler errors.
  4. Events can be included in an interface declaration, whereas a field cannot

Notes:

EventHandler is declared as the following delegate:

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.

This example, which uses EventHandler<ArgsSpecial>, can also be written using EventHandler instead.

Refer here for documentation about EventHandler

旧时模样 2024-08-09 08:08:55

除了语法和操作属性之外,还存在语义差异。

从概念上讲,委托是函数模板;也就是说,它们表达了函数必须遵守的合同,才能被视为代表的“类型”。

事件代表……好吧,事件。它们的目的是在发生事情时提醒某人,是的,它们遵循委托定义,但它们不是同一件事。

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

In addition to the syntactic and operational properties, there's also a semantical difference.

Delegates are, conceptually, function templates; that is, they express a contract a function must adhere to in order to be considered of the "type" of the delegate.

Events represent ... well, events. They are intended to alert someone when something happens and yes, they adhere to a delegate definition but they're not the same thing.

Even if they were exactly the same thing (syntactically and in the IL code) there will still remain the semantical difference. In general I prefer to have two different names for two different concepts, even if they are implemented in the same way (which doesn't mean I like to have the same code twice).

灵芸 2024-08-09 08:08:55

这是另一个值得参考的好链接。
简而言之

,从文章中可以看出 - 事件是对委托的封装。

引用文章:

假设事件在 C#/.NET 中不作为概念存在。另一个类如何订阅事件?三个选项:

  1. 公共委托变量

  2. 由属性支持的委托变量

  3. 具有 AddXXXHandler 和 RemoveXXXHandler 方法的委托变量

选项 1 显然很糟糕,因为我们厌恶公共变量的所有正常原因。

选项 2 稍好一些,但允许订阅者有效地相互覆盖 - 编写 someInstance.MyEvent = eventHandler; 太容易了;这将替换任何现有的事件处理程序,而不是添加新的事件处理程序。此外,您还需要编写属性。

选项 3 基本上是事件为您提供的,但具有有保证的约定(由编译器生成并由 IL 中的额外标志支持)和“自由”实现(如果您对类似字段事件的语义感到满意)给你。订阅和取消订阅事件被封装,不允许任意访问事件处理程序列表,并且语言可以通过提供声明和订阅的语法来使事情变得更简单。

Here is another good link to refer to.
http://csharpindepth.com/Articles/Chapter2/Events.aspx

Briefly, the take away from the article - Events are encapsulation over delegates.

Quote from article:

Suppose events didn't exist as a concept in C#/.NET. How would another class subscribe to an event? Three options:

  1. A public delegate variable

  2. A delegate variable backed by a property

  3. A delegate variable with AddXXXHandler and RemoveXXXHandler methods

Option 1 is clearly horrible, for all the normal reasons we abhor public variables.

Option 2 is slightly better, but allows subscribers to effectively override each other - it would be all too easy to write someInstance.MyEvent = eventHandler; which would replace any existing event handlers rather than adding a new one. In addition, you still need to write the properties.

Option 3 is basically what events give you, but with a guaranteed convention (generated by the compiler and backed by extra flags in the IL) and a "free" implementation if you're happy with the semantics that field-like events give you. Subscribing to and unsubscribing from events is encapsulated without allowing arbitrary access to the list of event handlers, and languages can make things simpler by providing syntax for both declaration and subscription.

愿与i 2024-08-09 08:08:55

活动与代表之间存在多大的误会!!!委托指定类型(例如接口),而事件只是一种成员(例如字段、属性等)。而且,就像任何其他类型的成员一样,事件也有类型。然而,在事件的情况下,事件的类型必须由委托指定。例如,您不能声明接口定义的类型的事件。

最后,我们可以做出以下观察:事件的类型必须由委托定义。这是事件和委托之间的主要关系,在 II.18 定义事件 部分中进行了描述/Ecma-335.htm" rel="noreferrer">ECMA-335 (CLI) 分区 I 到 VI

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

但是,这一事实并不意味着事件使用支持委托字段。事实上,事件可以使用您选择的任何不同数据结构类型的支持字段。如果您在 C# 中显式实现事件,则可以自由选择存储事件处理程序的方式(请注意,事件处理程序类型的实例。事件,而这又必须是委托类型——来自之前的观察)。但是,您可以将这些事件处理程序(它们是委托实例)存储在诸如列表或字典或任何其他数据结构中,甚至存储在后备委托中场地。但不要忘记,使用委托字段并不是强制性的。

What a great misunderstanding between events and delegates!!! A delegate specifies a TYPE (such as a class, or an interface does), whereas an event is just a kind of MEMBER (such as fields, properties, etc). And, just like any other kind of member an event also has a type. Yet, in the case of an event, the type of the event must be specified by a delegate. For instance, you CANNOT declare an event of a type defined by an interface.

Concluding, we can make the following Observation: the type of an event MUST be defined by a delegate. This is the main relation between an event and a delegate and is described in the section II.18 Defining events of ECMA-335 (CLI) Partitions I to VI:

In typical usage, the TypeSpec (if present) identifies a delegate whose signature matches the arguments passed to the event’s fire method.

However, this fact does NOT imply that an event uses a backing delegate field. In truth, an event may use a backing field of any different data structure type of your choice. If you implement an event explicitly in C#, you are free to choose the way you store the event handlers (note that event handlers are instances of the type of the event, which in turn is mandatorily a delegate type---from the previous Observation). But, you can store those event handlers (which are delegate instances) in a data structure such as a List or a Dictionary or any other else, or even in a backing delegate field. But don’t forget that it is NOT mandatory that you use a delegate field.

软的没边 2024-08-09 08:08:55

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


举一个简单、具体的例子总是对我有帮助。这是社区的一个。首先,我将展示如何单独使用委托来完成事件为我们所做的事情。然后我将展示相同的解决方案如何与 EventHandler 实例一起使用。然后我解释为什么我们不想做我在第一个例子中解释的事情。这篇文章的灵感来自 John Skeet 的一篇文章

示例 1:使用公共委托

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

在此处输入图像描述

以下是其工作原理。我们有三个文件可以帮助我们将其组合在一起:

  • 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`.
}

好的。这就是您如何在不使用事件而仅使用委托的情况下实现此功能。我们只是将一个公共委托放入一个类中——您可以将其设为静态或单例,或其他任何形式。伟大的。

但是,但是,但是,我们不想做我上面描述的事情。因为出于很多很多原因,公共字段不好。那么我们有什么选择呢?正如 John Skeet 所描述的,这里是我们的选择:

  1. 公共委托变量(这就是我们上面所做的。不要这样做。我刚刚告诉你上面为什么它不好)
  2. 将委托放入带有 get/set 的属性中(这里的问题是订阅者可以相互覆盖 - 因此我们可以向委托订阅一堆方法,然后我们可能会意外地说 PersonChangedDel = null,从而消除所有其他订阅另一个问题。 剩下的问题是,由于用户有权访问委托,因此他们可以调用调用列表中的目标——我们不希望外部用户有权访问何时引发我们的事件

这里 第三个选项本质上是事件为我们提供的。当我们声明 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 });
        }
    }
}

请注意,如果您按 F12 键, EventHandler,它将向您显示定义只是一个带有额外“发送者”对象的通用化委托:

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 作为参数,所以我创建了这个类其中只有一个属性:

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

希望这能让您了解为什么我们有事件以及它们作为委托有何不同(但功能相同)。

NOTE: If you have access to C# 5.0 Unleashed, read the "Limitations on Plain Use of Delegates" in Chapter 18 titled "Events" to understand better the differences between the two.


It always helps me to have a simple, concrete example. So here's one for the community. First I show how you can use delegates alone to do what Events do for us. Then I show how the same solution would work with an instance of EventHandler. And then I explain why we DON'T want to do what I explain in the first example. This post was inspired by an article by John Skeet.

Example 1: Using public delegate

Suppose I have a WinForms app with a single drop-down box. The drop-down is bound to an List<Person>. Where Person has properties of Id, Name, NickName, HairColor. On the main form is a custom user control that shows the properties of that person. When someone selects a person in the drop-down the labels in the user control update to show the properties of the person selected.

enter image description here

Here is how that works. We have three files that help us put this together:

  • Mediator.cs -- static class holds the delegates
  • Form1.cs -- main form
  • DetailView.cs -- user control shows all details

Here is the relevant code for each of the classes:

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

Here is our user control:

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;

    }
}

Finally we have the following code in our Form1.cs. Here we are Calling OnPersonChanged, which calls any code subscribed to the delegate.

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`.
}

Ok. So that's how you would get this working without using events and just using delegates. We just put a public delegate into a class -- you can make it static or a singleton, or whatever. Great.

BUT, BUT, BUT, we do not want to do what I just described above. Because public fields are bad for many, many reason. So what are our options? As John Skeet describes, here are our options:

  1. A public delegate variable (this is what we just did above. don't do this. i just told you above why it's bad)
  2. Put the delegate into a property with a get/set (problem here is that subscribers could override each other -- so we could subscribe a bunch of methods to the delegate and then we could accidentally say PersonChangedDel = null, wiping out all of the other subscriptions. The other problem that remains here is that since the users have access to the delegate, they can invoke the targets in the invocation list -- we don't want external users having access to when to raise our events.
  3. A delegate variable with AddXXXHandler and RemoveXXXHandler methods

This third option is essentially what an event gives us. When we declare an EventHandler, it gives us access to a delegate -- not publicly, not as a property, but as this thing we call an event that has just add/remove accessors.

Let's see what the same program looks like, but now using an Event instead of the public delegate (I've also changed our Mediator to a singleton):

Example 2: With EventHandler instead of a public delegate

Mediator:

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

Notice that if you F12 on the EventHandler, it will show you the definition is just a generic-ified delegate with the extra "sender" object:

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

The User Control:

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;

    }
}

Finally, here's the Form1.cs code:

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

Because the EventHandler wants and EventArgs as a parameter, I created this class with just a single property in it:

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

Hopefully that shows you a bit about why we have events and how they are different -- but functionally the same -- as delegates.

你又不是我 2024-08-09 08:08:55

Delegate是一个类型安全的函数指针。事件是使用委托的发布者-订阅者设计模式的实现。

Delegate is a type-safe function pointer. Event is an implementation of publisher-subscriber design pattern using delegate.

烟若柳尘 2024-08-09 08:08:55

您还可以在接口声明中使用事件,但对于委托则不然。

You can also use events in interface declarations, not so for delegates.

世界如花海般美丽 2024-08-09 08:08:55

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

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

An event in .net is a designated combination of an Add method and a Remove method, both of which expect some particular type of delegate. Both C# and vb.net can auto-generate code for the add and remove methods which will define a delegate to hold the event subscriptions, and add/remove the passed in delegagte to/from that subscription delegate. VB.net will also auto-generate code (with the RaiseEvent statement) to invoke the subscription list if and only if it is non-empty; for some reason, C# doesn't generate the latter.

Note that while it is common to manage event subscriptions using a multicast delegate, that is not the only means of doing so. From a public perspective, a would-be event subscriber needs to know how to let an object know it wants to receive events, but it does not need to know what mechanism the publisher will use to raise the events. Note also that while whoever defined the event data structure in .net apparently thought there should be a public means of raising them, neither C# nor vb.net makes use of that feature.

愁杀 2024-08-09 08:08:55

用简单的方式定义事件:

事件是对委托的引用,有两个限制

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

以上两个是代表们的弱点,并在活动中得到解决。显示 fiddler 差异的完整代码示例位于 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();
    }


}

To define about event in simple way:

Event is a REFERENCE to a delegate with two restrictions

  1. Cannot be invoked directly
  2. Cannot be assigned values directly (e.g eventObj = delegateMethod)

Above two are the weak points for delegates and it is addressed in event. Complete code sample to show the difference in fiddler is here https://dotnetfiddle.net/5iR3fB .

Toggle the comment between Event and Delegate and client code that invokes/assign values to delegate to understand the difference

Here is the inline code.

 /*
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();
    }


}
信仰 2024-08-09 08:08:55

对于生活在 2020 年的人们来说,想要一个干净的答案...

定义:

  • delegate:定义一个函数指针。
  • 事件:定义
    • (1) 受保护接口,
    • (2) 运算(+=-=)、
    • (3) 优势:您不再需要使用 new 关键字。

关于形容词“受保护”:

// eventTest.SomeoneSay = null;              // Compile Error.
// eventTest.SomeoneSay = new Say(SayHello); // Compile Error.

另请注意 Microsoft 的此部分:https://learn.microsoft.com/en-us/dotnet/standard/events/#raise-multiple-events

代码示例:

使用 delegate :

public class DelegateTest
{
    public delegate void Say(); // Define a pointer type "void <- ()" named "Say".
    private Say say;

    public DelegateTest() {
        say  = new Say(SayHello);     // Setup the field, Say say, first.
        say += new Say(SayGoodBye);
        
        say.Invoke();
    }
    
    public void SayHello() { /* display "Hello World!" to your GUI. */ }
    public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
}

with event:

public class EventTest
{
    public delegate void Say();
    public event Say SomeoneSay;  // Use the type "Say" to define event, an 
                                  // auto-setup-everything-good field for you.
    public EventTest() {
         SomeoneSay += SayHello;
         SomeoneSay += SayGoodBye;

         SomeoneSay();
    }
    
    public void SayHello() { /* display "Hello World!" to your GUI. */ }
    public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
}

参考:

事件与委托 - 解释 C# 中事件和委托模式之间的重要区别以及它们为何有用。 >: https://dzone.com/articles/event-vs-delegate

For people live in 2020, and want a clean answer...

Definitions:

  • delegate: defines a function pointer.
  • event: defines
    • (1) protected interfaces, and
    • (2) operations(+=, -=), and
    • (3) advantage: you don't need to use new keyword anymore.

Regarding the adjective protected:

// eventTest.SomeoneSay = null;              // Compile Error.
// eventTest.SomeoneSay = new Say(SayHello); // Compile Error.

Also notice this section from Microsoft: https://learn.microsoft.com/en-us/dotnet/standard/events/#raising-multiple-events

Code Example:

with delegate:

public class DelegateTest
{
    public delegate void Say(); // Define a pointer type "void <- ()" named "Say".
    private Say say;

    public DelegateTest() {
        say  = new Say(SayHello);     // Setup the field, Say say, first.
        say += new Say(SayGoodBye);
        
        say.Invoke();
    }
    
    public void SayHello() { /* display "Hello World!" to your GUI. */ }
    public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
}

with event:

public class EventTest
{
    public delegate void Say();
    public event Say SomeoneSay;  // Use the type "Say" to define event, an 
                                  // auto-setup-everything-good field for you.
    public EventTest() {
         SomeoneSay += SayHello;
         SomeoneSay += SayGoodBye;

         SomeoneSay();
    }
    
    public void SayHello() { /* display "Hello World!" to your GUI. */ }
    public void SayGoodBye() { /* display "Good bye!" to your GUI. */ }
}

Reference:

Event vs. Delegate - Explaining the important differences between the Event and Delegate patterns in C# and why they're useful.: https://dzone.com/articles/event-vs-delegate

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文