事件不是字段 - 我不明白

P.B*_*key 52 c# events field

C#深度(迄今为止一本优秀的书)中,Skeet解释事件不是字段.我多次阅读本节,我不明白为什么这种区别有所不同.

我是那些混淆事件和委托实例的开发人员之一.在我看来,它们是一样的.这两种都不是间接的形式吗?我们可以组播两者.事件被设置为字段作为简写...确定.但是,我们正在添加或删除处理程序.将它们堆叠起来以便在事件触发时调用.我们不要对委托做同样的事情,叠加它们并调用invoke吗?

Eri*_*ert 47

其他答案基本上是正确的,但这是另一种看待它的方式:

我是那些混淆事件和委托实例的开发人员之一.在我看来,它们是一样的.

我想到了一个关于没有看到森林树木的古老谚语.我所做的区别是事件处于比委托实例字段更高的"语义级别".一个事件告诉消费者类型"你好,我是一个喜欢在发生事情时告诉你的类型".类型来源事件; 这是公共合同的一部分.

作为一个实现细节,该类选择如何跟踪谁有兴趣收听该事件,以及告诉订阅者该事件发生的内容和时间,是该类的业务.通常使用多播委托进行此操作,但这是一个实现细节.这是一个常见的实现细节,将两者混淆是合理的,但我们确实有两个不同的东西:公共表面和私有实现细节.

类似地,属性描述对象的语义:客户具有名称,因此Customer类具有Name属性.您可能会说"他们的名字"是客户的财产,但您永远不会说"他们的名字"是客户的一个领域 ; 这是特定类的实现细节,而不是关于业务语义的事实.通常将属性实现为字段是类机制的私有细节.

  • 切向:如果你可以从头开始重新启动C#和.NET,你会用IObservable <T>替换事件/委托吗? (5认同)

Ben*_*igt 41

属性也不是字段,尽管它们感觉像它们.它们实际上是一对具有特殊语法的方法(getter和setter).

事件类似于一对具有特殊语法的方法(订阅和取消订阅).

在这两种情况下,您通常在类中都有一个私有的"支持字段",它保存由getter/setter/subscribe/unsubscribe方法操纵的值.并且有一个自动实现的属性和事件语法,编译器为您生成支持字段和访问器方法.

目的也是相同的:属性提供对字段的受限访问,其中在存储新值之前运行某些验证逻辑.并且事件提供对委托字段的受限访问,其中消费者只能订阅或取消订阅,不能读取订阅者列表,也不能立即替换整个列表.

  • @dsolimano"overriding"可能是一个令人困惑的术语,因为这通常与多态性有关;**提供一个定制的**`add` /`remove`,当然 - 有点像自动实现的属性和常规属性之间的区别(类似于"自动实现的属性"的事件的术语是"字段式事件" ,顺便说一句) (4认同)

ang*_*son 24

让我们考虑一下声明事件的两种方法.

您可以使用显式add/ remove方法声明事件,也可以声明没有此类方法的事件.

换句话说,你声明这样的事件:

public event EventHandlerType EventName
{
    add
    {
        // some code here
    }
    remove
    {
        // some code here
    }
}
Run Code Online (Sandbox Code Playgroud)

或者你这样声明:

public event EventHandlerType EventName;
Run Code Online (Sandbox Code Playgroud)

问题是,在某些方面它们是相同的,在其他方面,它们是完全不同的.

从外部代码的角度来看,即发布事件的类之外的代码,它们完全相同.要订阅活动,请调用方法.要取消订阅,您可以使用其他方法.

不同之处在于,在上面的第二个示例代码中,这些方法将由编译器为您提供,但是,它仍然是如何.要订阅该事件,请调用方法.

但是,在C#中这样做的语法是相同的,你可以这样做:

objectInstance.EventName += ...;
Run Code Online (Sandbox Code Playgroud)

要么:

objectInstance.EventName -= ...;
Run Code Online (Sandbox Code Playgroud)

所以从"外部视角"来看,这两种方式完全没有区别.

但是,在课堂上,有一个区别.

如果您尝试访问类中的EventName标识符,那么实际上您指的field是支持该属性的标识符,但前提是您使用的语法未明确声明add/ remove方法.

典型的模式是这样的:

public event EventHandlerType EventName;

protected void OnEventName()
{
    var evt = EventName;
    if (evt != null)
        evt(this, EventArgs.Empty);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,当您提到时EventName,您实际上是指包含类型委托的字段EventHandlerType.

但是,如果你已经显式声明了add/ removemethods,那么引用类中的EventName标识符就像在类之外一样,因为编译器不能保证它知道存储订阅的字段或任何其他机制. .


Han*_*ant 9

事件是代理的访问者.就像一个属性是一个字段的访问者.使用完全相同的实用程序,它可以防止代码弄乱委托对象.就像属性具有get和set访问器一样,事件具有添加和删除访问器.

它的行为与属性有所不同,如果您不自己编写添加和删除访问器,则编译器会自动生成它们.包括存储委托对象的私有备份字段.类似于自动属性.

你不经常这样做,但肯定并不罕见..NET框架通常这样做,例如Winforms控件的事件存储在EventHandlerList中,添加/删除访问器通过其AddHandler()和RemoveHandler()方法操作该列表.优点是所有事件(有很多)只需要类中的一个字段.

  • Skeet的观点是事件是提供对值的访问的方法,而不是值本身.就像一个属性,但具有不同的操作.该值通常(但不总是)存储在字段中.再次,就像一个属性.说事件可能没有支持字段所涉及的迂腐程度也不包括作出一个属性是字段的访问者的一般声明. (4认同)
  • 它和你答案中的第二句一样真实. (3认同)