C#:显式添加/删除事件!= 典型事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1015166/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C#: event with explicity add/remove != typical event?
提问by
I have declared a generic event handler
我已经声明了一个通用的事件处理程序
public delegate void EventHandler();
to which I have added the extension method 'RaiseEvent':
我添加了扩展方法“RaiseEvent”:
public static void RaiseEvent(this EventHandler self) {
if (self != null) self.Invoke();
}
When I define the event using the typical syntax
当我使用典型语法定义事件时
public event EventHandler TypicalEvent;
then I can call use the extension method without problems:
然后我可以毫无问题地调用使用扩展方法:
TypicalEvent.RaiseEvent();
But when I define the event with explicit add/remove syntax
但是当我使用显式添加/删除语法定义事件时
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
add { _explicitEvent += value; }
remove { _explicitEvent -= value; }
}
then the extension method does not exist on the event defined with explicit add/remove syntax:
那么扩展方法在使用显式添加/删除语法定义的事件上不存在:
ExplicitEvent.RaiseEvent(); //RaiseEvent() does not exist on the event for some reason
And when I hover over to event to see the reason it says:
当我将鼠标悬停在事件上以查看原因时,它说:
The event 'ExplicitEvent' can only appear on the left hand side of += or -=
事件 'ExplicitEvent' 只能出现在 += 或 -= 的左侧
Why should an event defined using the typical syntax be different from an event defined using the explicit add/remove syntax and why extension methods do not work on the latter?
为什么使用典型语法定义的事件与使用显式添加/删除语法定义的事件不同,为什么扩展方法不适用于后者?
EDIT: I found I can work around it by using the private event handler directly:
编辑:我发现我可以通过直接使用私有事件处理程序来解决它:
_explicitEvent.RaiseEvent();
But I still don't understand why I cannot use the event directly like the event defined using the typical syntax. Maybe someone can enlighten me.
但是我仍然不明白为什么我不能像使用典型语法定义的事件那样直接使用该事件。也许有人可以启发我。
采纳答案by TcKs
Because you can do this (it's non-real-world sample, but it "works"):
因为你可以这样做(它是非现实世界的样本,但它“有效”):
private EventHandler _explicitEvent_A;
private EventHandler _explicitEvent_B;
private bool flag;
public event EventHandler ExplicitEvent {
add {
if ( flag = !flag ) { _explicitEvent_A += value; /* or do anything else */ }
else { _explicitEvent_B += value; /* or do anything else */ }
}
remove {
if ( flag = !flag ) { _explicitEvent_A -= value; /* or do anything else */ }
else { _explicitEvent_B -= value; /* or do anything else */ }
}
}
How can the compiler know what it should do with "ExplicitEvent.RaiseEvent();"? Answer: It can't.
编译器如何知道它应该用“ExplicitEvent.RaiseEvent();”做什么?答:不能。
The "ExplicitEvent.RaiseEvent();" is only syntax sugar, which can be predicated only if the event is implicitly implemented.
“ExplicitEvent.RaiseEvent();” 只是语法糖,只有隐式实现了事件才可以断言。
回答by itowlson
The "plain" declaration for TypicalEvent does some compiler trickery. It creates an event metadata entry, add and remove methods and a backing field. When your code refers to TypicalEvent, the compiler translates it into a reference to the backing field; when external code refers to TypicalEvent (using += and -=), the compiler translates it into a reference to the add or remove method.
典型事件的“普通”声明做了一些编译器技巧。它创建一个事件元数据条目、添加和删除方法以及一个支持字段。当您的代码引用典型事件时,编译器将其转换为对支持字段的引用;当外部代码引用TypicalEvent(使用+= 和-=)时,编译器将其转换为对add 或remove 方法的引用。
The "explicit" declaration bypasses this compiler trickery. You are spelling out the add and remove methods and the backing field: indeed, as TcKs points out, there may not even bea backing field (this is a common reason for using the explicit form: see e.g. events in System.Windows.Forms.Control). Therefore the compiler can no longer quietly translate the reference to TypicalEvent into a reference to the backing field: if you want the backing field, the actual delegate object, you have to reference the backing field directly:
“显式”声明绕过了这个编译器技巧。您列明添加和删除方法和支持字段:的确,作为第三文化孩子所指出的,可能没有甚至是支持字段(这是使用显式形式的常见原因:看到System.Windows.Forms的如事件。控制)。因此,编译器不能再悄悄地将典型事件的引用转换为对支持字段的引用:如果您想要支持字段,即实际的委托对象,则必须直接引用支持字段:
_explicitEvent.RaiseEvent()
回答by Jon Skeet
When you create a "field-like" event, like this:
当您创建“类似字段”的事件时,如下所示:
public event EventHandler Foo;
the compiler generates a field andan event. Within the source code of the class which declares the event, any time you refer to Foo
the compiler understand that you're referring to the field. However, the field is private, so any time you refer to Foo
from otherclasses, it refers to the event (and therefore the add/remove code).
编译器生成一个字段和一个事件。在声明事件的类的源代码中,任何时候您引用Foo
编译器都明白您指的是field。但是,该字段是私有的,因此任何时候您Foo
从其他类中引用时,它都会引用事件(以及添加/删除代码)。
If you declare your own explicit add/remove code, you don't get an auto-generated field. So, you've only got an event, and you can't raise an event directly in C# - you can only invoke a delegate instance. An event isn't a delegate instance, it's just an add/remove pair.
如果您声明自己的显式添加/删除代码,则不会获得自动生成的字段。所以,你只有一个事件,你不能直接在 C# 中引发一个事件 - 你只能调用一个委托实例。事件不是委托实例,它只是一个添加/删除对。
Now, your code contained this:
现在,您的代码包含以下内容:
public EventHandler TypicalEvent;
This is slightly different still - it wasn't declaring an eventat all - it was declaring a public fieldof the delegate type EventHandler
. Anyonecan invoke that, because the value is just a delegate instance. It's important to understand the difference between a field and an event. You should never write this kind of code, just as I'm sure you don't normally have public fields of other types such as string
and int
. Unfortunately it's an easy typo to make, and a relatively hard one to stop. You'd only spot it by noticing that the compiler was allowing you to assign or use the value from another class.
这仍然略有不同 - 它根本没有声明事件- 它声明了委托类型的公共字段EventHandler
。任何人都可以调用它,因为该值只是一个委托实例。了解字段和事件之间的区别很重要。你永远不应该写这种代码,就像我确定你通常没有其他类型的公共字段,比如string
and int
。不幸的是,这是一个容易犯的错字,而且相对难以停止。您只能通过注意到编译器允许您分配或使用来自另一个类的值来发现它。
See my article on events and delegatesfor more information.
有关更多信息,请参阅我关于事件和委托的文章。
回答by Yochai Timmer
That's because you're not looking at it right. The logic is the same as in Properties. Once you've set the add/remove it's no longer an actual event, but a wrapper to expose the actual event (events can only be triggered from inside the class itself, so you always have access locally to the real event).
那是因为你没有正确看待它。逻辑与属性中的相同。一旦设置了添加/删除,它就不再是实际事件,而是用于公开实际事件的包装器(事件只能从类本身内部触发,因此您始终可以在本地访问真实事件)。
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
add { _explicitEvent += value; }
remove { _explicitEvent -= value; }
}
private double seconds;
public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
In both cases the member with the get/set or add/remove property doesn't really contain any data. You need a "real" private member to contain the actual data. The properties just allow you program extra logic when exposing the members to outside world.
在这两种情况下,具有 get/set 或 add/remove 属性的成员实际上并不包含任何数据。您需要一个“真正的”私有成员来包含实际数据。这些属性只允许您在将成员暴露给外部世界时编写额外的逻辑。
A good example for WHY you'd want to do it, is to stop extra computation when it's not needed (no one is listening to the event).
为什么要这样做的一个很好的例子是,在不需要时停止额外的计算(没有人在监听事件)。
For example, lets say the events are triggered by a timer, and we don't want the timer to work if no-one is registered to the event:
例如,假设事件是由计时器触发的,如果没有人注册事件,我们不希望计时器工作:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent
{
add
{
if (_explicitEvent == null) timer.Start();
_explicitEvent += value;
}
remove
{
_explicitEvent -= value;
if (_explicitEvent == null) timer.Stop();
}
}
You'd probably want to lock the add/remove with an object (an afterthought)...
你可能想用一个对象来锁定添加/删除(事后的想法)......