在C#深入浅出(到目前为止非常好的一本书)中,Skeet解释了事件不是字段。我读了这一部分很多次,但我不明白为什么区别会有任何影响。
我是那些混淆事件和委托实例的开发人员之一。在我看来,它们是相同的。难道它们不都是一种间接形式吗?我们可以广播两者。事件被设置为一个字段作为简写...当然。但是,我们正在添加或删除处理程序。堆叠它们以在事件触发时调用。我们不是也用委托做同样的事情,将它们堆叠起来并调用invoke吗?
在C#深入浅出(到目前为止非常好的一本书)中,Skeet解释了事件不是字段。我读了这一部分很多次,但我不明白为什么区别会有任何影响。
我是那些混淆事件和委托实例的开发人员之一。在我看来,它们是相同的。难道它们不都是一种间接形式吗?我们可以广播两者。事件被设置为一个字段作为简写...当然。但是,我们正在添加或删除处理程序。堆叠它们以在事件触发时调用。我们不是也用委托做同样的事情,将它们堆叠起来并调用invoke吗?
属性和字段不同,尽管它们感觉相似。实际上,它们是一对带有特殊语法的方法(getter和setter)。
事件类似于一对带有特殊语法的方法(subscribe和unsubscribe)。
在这两种情况下,通常你的类中会有一个私有的“后备字段”,它保存着由getter/setter/subscribe/unsubscribe方法操作的值。并且,对于属性和事件都有自动实现的语法,编译器会为你生成后备字段和访问器方法。
其目的也是相同的:属性提供对字段的受限访问,在存储新值之前运行一些验证逻辑。事件提供对委托字段的受限访问,其中消费者只能订阅或取消订阅,不能读取订阅者列表,也不能一次替换整个列表。
EventHandlerList
,它是大多数事件的“后台支持者”(并且可以通过.Events
提供给子类使用)。只有1个字段 - 很多很多事件。 - Marc Gravelladd
和remove
方法来完成的 - 例如http://msdn.microsoft.com/en-us/library/ak9w5846.aspx的第二个示例。 - dsolimano让我们考虑两种声明事件的方式。
一种是使用显式的add
/remove
方法声明事件,另一种是不使用这些方法声明事件。
换句话说,你可以像这样声明事件:
public event EventHandlerType EventName
{
add
{
// some code here
}
remove
{
// some code here
}
}
或者你可以这样声明:
public event EventHandlerType EventName;
事实上,它们在某些方面是相同的,但在其他方面则完全不同。objectInstance.EventName += ...;
或者:
objectInstance.EventName -= ...;
从“外部的角度”看,这两种方法没有任何区别。
然而,在类内部有所区别。
如果您尝试在类内部访问EventName
标识符,您实际上是在引用支持属性的字段
,但仅当您使用不显式声明add
/remove
方法的语法时才是如此。
一种典型的模式如下:
public event EventHandlerType EventName;
protected void OnEventName()
{
var evt = EventName;
if (evt != null)
evt(this, EventArgs.Empty);
}
在这种情况下,当您引用 EventName
时,实际上是在引用一个持有类型为 EventHandlerType
的 委托 的字段。
然而,如果您显式声明了 add
/remove
方法,并且在类内部引用 EventName
标识符,那么就像在类外部一样,编译器无法保证它知道您存储订阅的字段或任何其他机制。
我可以补充前面的回答,委托可以在命名空间范围内(类外)声明,而事件只能在类内声明。 这是因为委托是一个类!
另一个区别是,对于事件,包含它的类是唯一可以触发它的类。你可以通过包含它的类来订阅/取消订阅它,但不能触发它(与委托相比)。因此,现在也许你可以理解为什么惯例是将其包装在protected virtual OnSomething(object sender, EventArgs e)
中了。这是为了让后代能够覆盖触发的实现。