【问题标题】:How are delegates linked to events?代表如何与事件相关联?
【发布时间】:2017-03-09 08:12:45
【问题描述】:

我已经阅读了很多关于委托和事件的材料和示例,但我真的很难理解 C# 中的事件。我知道委托实际上是方法指针,您可以在调用委托时添加方法然后一起执行,假设它符合委托的输入参数和返回类型。

(我很清楚堆栈溢出时会解释事件,但甚至没有帮助我理解)

我将分享我的代码,它正在运行,但我不明白

namespace Delegates2 {
    class Program {
        static void Main(string[] args) {
            Car mazda = new Car("Mazda");
            Person chris = new Person();
            mazda.Drive += chris.OnPersonDrive;
            mazda.StartJourney();
            Console.ReadKey();
        }

    }
    public delegate void DriverCarEventHandler(object obj, EventArgs e);
    public class Car
    {
        public string Name { get; }
        public event DriverCarEventHandler Drive;    

        public Car(string name) {
            Name = name;
        }
        public void StartJourney() {
            OnDrive();
        }
        protected virtual void OnDrive() {
            if (Drive != null) {
                Drive(this, EventArgs.Empty);
            }
        }
    }

    public class Person
    {
        public void OnPersonDrive(Object source, EventArgs e) {
            Console.WriteLine("Driving....");
        } 
    }
} 

这是我所有的困惑点......

  • 委托声明中的“Object obj”和“EventArgs e”有何作用 代表?因为学习似乎这些都是空的。什么可能是这些可能需要不同的示例?
  • 当您基于委托创建事件时,是否 事件现在存储所有指向方法的指针还是它仍然是 代表?我听说过有关将订阅者添加到 事件。这些“订阅者”是否只是以与您相同的方式使用的方法 向代表添加方法?
  • 显然 OnDrive() 方法正在“引发”事件。 “引发”事件与执行事件相同吗 事件?那么执行附加到事件的所有订阅者?
  • 为什么当你发起一个事件时它必须符合 委托输入参数? (这个,EventArgs.Empty)。难道不是 只有需要匹配此格式的订阅者?

【问题讨论】:

  • “我知道委托实际上是方法指针,您可以添加方法,然后在调用委托时一起执行”——这正是事件的含义。它们是从您的对象公开的委托,您可以将方法指针添加到对象本身希望引发它们时执行的方法指针。委托和事件之间的主要区别在于,事件只能由持有它们的类引发(执行),而委托可以由任何代码执行。
  • 您不需要在委托中添加对象和事件参数......它们是您自己制作的事件。您可以不添加任何参数或仅添加字符串或其他内容 -_-
  • @PeterDuniho Point 仍然有效,您可以为自己的委托选择自己的参数
  • @EpicKip:抱歉,我误解了您的评论,并认为您的意思是您可以对事件使用非委托类型。

标签: c# events delegates


【解决方案1】:

尽可能简短地回答您的问题,以避免使您的问题“过于宽泛”……

•委托声明中的“Object obj”和“EventArgs e”代表什么?因为学习似乎这些都是空的。什么可能是这些可能需要不同的示例?

obj 应始终为非 null,并且应设置为引发事件的对象的引用。 e 应该是 EventArgs 或派生类的实例,包含与事件相关的信息。

需要注意的是,这种模式是严格约定的。 IE。不是任何人强制执行的,也不是必需的。 C# event 可以使用 any 委托类型。 EventHandler 委托类型只是在 .NET 中实现事件的标准方法的基础。

•当您基于委托创建事件时,该事件现在是存储所有指向方法的指针还是仍然是委托?我听说过有关向活动添加订阅者的术语。这些“订阅者”是否只是方法,就像您向委托添加方法一样?

大多数事件都是由编译器隐式实现的。但重要的是要了解,当您使用 event 关键字时,您同时为事件声明了 addremove 方法。这些类似于属性的getset 方法。默认实现仅采用传入的委托实例,并将其附加到事件支持字段的现有值(也为您隐式生成)的现有值中或将其删除,分别用于 addremove 方法。

事件的默认实现将其存储在一个简单的delegate 类型字段中。还有一些更复杂的事件实现,例如将事件处理程序引用放在字典中,或者使用弱引用。

•显然 OnDrive() 方法正在“引发”事件。 “引发”事件与执行事件相同吗?那么执行附加到事件的所有订阅者?

是的,引发事件只是调用已添加(订阅)到该事件的委托。 .NET 委托类型都是“多播”委托,因此单个委托实例可以表示多个调用目标。调用一个委托实例将自动调用所有个单独的调用目标。

按照惯例,名为OnXXX() 的方法(其中XXX 是某个事件的名称)将始终引发该事件。但没有要求这样做。一个明显的例外是当事件字段为null 时,即没有处理程序订阅该事件。在这种情况下,显然没有引发事件。另一个更具体的真实示例是 Winforms API 中 Control 对象公开的各种事件,这些事件有几个例外的规则(与视觉状态变化有关的事件,如当前字体、背景颜色、启用状态等),缩短事件引发逻辑并在对象或其任何祖先对象(即父母、祖父母等)正在被处置时返回而不引发事件。

•为什么当您引发一个事件时,它必须符合委托输入参数? (这个,EventArgs.Empty)。不是只有订阅者才需要匹配这种格式吗?

要调用委托,代码必须将必要的参数传递给它,就像当你调用any方法时,你需要将必要的参数传递给它(即使有默认值,编译器也会生成确保提供所有参数值所需的代码。

如果委托的调用者没有传递参数值,那么最终传递给目标方法的值从何而来?

【讨论】:

  • 非常感谢彼得。因此,出于学习的目的,将事件处理程序视为存储委托字段的对象是否公平,并且该对象具有用于该对象的添加或删除方法,并且只有该委托?然后,当您添加订阅者时,您将转到 EventHandler -> EventHandler 委托来存储订阅者(作为方法)。 Drive != null 是否引用了与事件相关联的委托的存在,还是引用了事件处理程序委托所指向的订阅者/方法的存在?
  • 我建议添加到 OnDrive() 答案中,以说明 OnDrive() 方法没有义务实际引发事件 - 这取决于它自己的内部逻辑是否应该引发事件提出与否。
  • @Konzy262:我不认为event 是一个对象,因为在语言中有实际“对象”的上下文中这可能会造成混淆。 IE。 eventclassstruct 更像是一个属性、字段或方法(即真正的“对象”)。但是,是的,否则你是对的......event 维护,但是是需要的,一种跟踪添加到事件/从事件中删除的代表的方法。
  • @Konzy262:表达式Drive != null 是编译器提供的事件的自动实现的产物。使用自动实现,编译器实际上会生成 两个 具有相同名称的类成员,例如Drive。对于类外部的代码,该名称指的是类的event 成员。但是对于类内部的代码,名称指的是事件的支持字段,即事件的addremove方法用来跟踪订阅者的东西.作为一个委托类型,它可以与null 进行比较,并使用正常的委托调用语法进行调用。
  • @PeterDuniho - 在某些情况下,事件会被静音 - 例如,我刚刚检查了 System.Windows.Forms.Control.cs 的源代码,OnBackColorChanged 方法在开头有一个可以防止事件触发的守卫。我敢肯定还有很多其他人。所以仅仅通过调用On* 方法并不意味着事件会触发。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 2011-05-31
  • 1970-01-01
  • 2012-11-10
相关资源
最近更新 更多