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


当前回答

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

其他回答

除了语法和操作属性之外,还有语义上的差异。

从概念上讲,委托是函数模板;也就是说,它们表达了一个函数必须遵守的契约,以便被认为是委托的“类型”。

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

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

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

最后,我们可以做以下观察:事件的类型必须由委托定义。这是事件和委托之间的主要关系,在第二节中描述。18定义ECMA-335 (CLI)分区I到VI的事件:

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

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.

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

委托的示例(在本例中是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是一个类型安全的函数指针。事件是使用委托的发布者-订阅者设计模式的实现。