委托和事件之间的区别是什么?两者不都持有可以执行的函数的引用吗?


当前回答

这是另一个很好的参考链接。 http://csharpindepth.com/Articles/Chapter2/Events.aspx

简单地说,本文的要点是——事件是对委托的封装。

引用自文章:

Suppose events didn't exist as a concept in C#/.NET. How would another class subscribe to an event? Three options: A public delegate variable A delegate variable backed by a property 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.

其他回答

为了理解它们的区别,你可以看看这两个例子

委托的示例(在本例中是Action -这是一种不返回值的委托)

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#中使用事件。

你的动物职业会这样改变:

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

差异:

You aren't using a public property but a public field (using events, the compiler protects your fields from unwanted access) 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. 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. Events can be included in an interface declaration, whereas a field cannot

注:

EventHandler被声明为如下委托:

public delegate void EventHandler (object sender, EventArgs e)

它接受一个发送者(Object类型)和事件参数。如果来自静态方法,则发送者为空。

这个例子使用了EventHandler<ArgsSpecial>,也可以使用EventHandler来代替。

请参阅这里有关EventHandler的文档

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

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

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

定义:

Delegate:定义一个函数指针。 事件:定义 (1)受保护接口,以及 (2)操作(+=,-=),和 (3)优点:你不再需要使用new关键字。

关于形容词protected:

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

还要注意来自微软的这一部分:https://learn.microsoft.com/en-us/dotnet/standard/events/#raising-multiple-events

代码示例:

委托:

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. */ }
}

事件:

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

您还可以在接口声明中使用事件,但委托则不是这样。