问题
I have three objects ObjectA has an ObjectB, ObjectB has an ObjectC. When ObjectC fires an event I need ObjectA to know about it, so this is what I've done...
public delegate void EventFiredEventHandler();
public class ObjectA
{
ObjectB objB;
public ObjectA()
{
objB = new ObjectB();
objB.EventFired += new EventFiredEventHandler(objB_EventFired);
}
private void objB_EventFired()
{
//Handle the event.
}
}
public class ObjectB
{
ObjectC objC;
public ObjectB()
{
objC = new ObjectC();
objC.EventFired += new EventFiredEventHandler(objC_EventFired);
objC.FireEvent();
}
public event EventFiredEventHandler EventFired;
protected void OnEventFired()
{
if(EventFired != null)
{
EventFired();
}
}
private void objC_EventFired()
{
//objC fired an event, bubble it up.
OnEventFired();
}
}
public class ObjectC
{
public ObjectC(){}
public void FireEvent()
{
OnEventFired();
}
public event EventFiredEventHandler EventFired;
protected void OnEventFired()
{
if(EventFired != null)
{
EventFired();
}
}
}
Is this the proper way to handle this, or is there a better way? I don't want ObjectA to know about ObjectC at all, only that it raised an event.
回答1:
Another approach, is to wrap it using add/remove:
public class ObjectB
{
ObjectC objC;
public ObjectB()
{
objC = new ObjectC();
}
public event EventFiredEventHandler EventFired
{
add { this.objC.EventFired += value; }
remove { this.objC.EventFired -= value; }
}
}
回答2:
That's the way I do it. however I would recommend change your firing mechanism to this to make it thread safe
protected void OnEventFired()
{
var tmpEvent = EventFired;
if(tmpEvent != null)
{
tmpEvent();
}
}
This keeps it from failing if EventFired becomes null between the null check and the firing.
Also it is somewhat of a standard to follow the EventHandler pattern for your event delegates.
protected virtual void OnEventFired(EventArgs e)
{
var tmpEvent = EventFired;
if(tmpEvent != null)
{
tmpEvent(this, EventArgs.e);
}
}
I was wrong about the threadsafe pattern, here is the full threadsafe event pattern
/// <summary>
/// Delegate backing the SomeEvent event.
/// </summary>
SomeEventHandler someEvent;
/// <summary>
/// Lock for SomeEvent delegate access.
/// </summary>
readonly object someEventLock = new object();
/// <summary>
/// Description for the event
/// </summary>
public event SomeEventHandler SomeEvent
{
add
{
lock (someEventLock)
{
someEvent += value;
}
}
remove
{
lock (someEventLock)
{
someEvent -= value;
}
}
}
/// <summary>
/// Raises the SomeEvent event
/// </summary>
protected virtual OnSomeEvent(EventArgs e)
{
SomeEventHandler handler;
lock (someEventLock)
{
handler = someEvent;
}
if (handler != null)
{
handler (this, e);
}
}
回答3:
As other answers have stated, this is they way to do it.
But you can go beyond!!! I've just implemented a good data structure on it, and it's like to give you a spin on it.
Would be nice to have an automatic event bubbling? You could implement it using Reflection. My way is to define an Interface/Base class which declares an event (or a set of events). Then, the parameterless constructor of a base class will iterate other its properties/fields, and register automatically the members events for event propagation.
There are some restriction on design, but if you have a deep structure and/or many (structured) events, it could be nice to have everything setup without any additional line of code.
An initial base class could be:
class BaseObject {
public BaseObject() {
FieldInfo[] fInfos = this.GetType().GetFields(...);
foreach (FieldInfo fInfo in fInfos) {
object fInfoValue = fInfo.GetValue(this, null);
if (fInfoValue is BaseObject) {
BaseObject bMemberObject = (BaseObject)fInfoValue;
bMemberObject.MyEvent += new EventHandler(delegate() {
if (this.MyEvent != null)
MyEvent();
});
}
}
public event MyEvent = null;
}
Of course, as already suggested, follow the event delegate delegate(object sender, EventArgs args) (I've used a simpler event for clarity). Naturally, is implicit that you classes A, B and C derives directly from BaseObject.
Note that any logic could be implemented to bind structured events (you could be the nested event registration using the name and/or other reflected properties.
来源:https://stackoverflow.com/questions/4349195/what-is-the-preferred-way-to-bubble-events