【问题标题】:What is the preferred way to bubble events?冒泡事件的首选方式是什么?
【发布时间】:2011-05-19 22:48:22
【问题描述】:

我有三个对象 ObjectA 有一个 ObjectB,ObjectB 有一个 ObjectC。当 ObjectC 触发一个事件时,我需要 ObjectA 知道它,所以这就是我所做的......

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();
        }
    }
}

这是处理这个问题的正确方法,还是有更好的方法?我根本不想让 ObjectA 知道 ObjectC,只知道它引发了一个事件。

【问题讨论】:

  • 这是一种有效的方法。

标签: c# events event-bubbling


【解决方案1】:

另一种方法是使用添加/删除来包装它:

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】:

我就是这样做的。但是我建议将您的触发机制更改为此以使其线程安全

protected void OnEventFired()
{
    var tmpEvent = EventFired;
    if(tmpEvent != null)
    {
        tmpEvent();
    }
}

如果 EventFired 在 null 检查和触发之间变为 null,这可以防止它失败。

此外,为您的活动代表遵循EventHandler pattern 也是一种标准。

protected virtual void OnEventFired(EventArgs e)
{
    var tmpEvent = EventFired;
    if(tmpEvent != null)
    {
        tmpEvent(this, EventArgs.e);
    }
}

我错了线程安全模式,这里是完整的线程安全事件模式

/// <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);
    }
}

【讨论】:

  • @Mike Cheel 正如我在帖子中所说,如果在 null 检查和实际触发之间取消订阅事件,则会抛出异常(我认为是 NullRefrence)。通过将事件分配给单独的变量,如果在此函数的中间取消订阅,事件仍将在上次触发。
  • 不是平时的签名protected virtual void OnMyEvent(MyEventArgs e)吗?
  • @Scott Chamberlain:你不会得到 NullReference 异常吗?
  • @Scott Chamberlain:没有遵循 EventHandler 模式,因为我很懒,不想打字。
  • @Tester101 这就是我的想法。如果有这么多订阅和取消订阅的事件,我会亲自将其包装在尝试中并处理异常。
【解决方案3】:

正如其他答案所述,这是他们的做法。

但你可以超越!!!我刚刚在它上面实现了一个很好的数据结构,它就像给你一个旋转。

让事件自动冒泡会很好吗?您可以使用反射来实现它。我的方法是定义一个接口/基类来声明一个事件(或一组事件)。然后,基类的无参构造函数将迭代其其他属性/字段,并自动注册成员事件以进行事件传播。

在设计上有一些限制,但如果您有一个深层结构和/或许多(结构化)事件,那么最好在没有任何额外代码行的情况下完成所有设置。

初始基类可以是:

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;

}

当然,如前所述,遵循事件委托delegate(object sender, EventArgs args)(为了清楚起见,我使用了一个更简单的事件)。 自然,你的类 ABC 直接从 BaseObject 派生。

请注意,可以实现任何逻辑来绑定结构化事件(您可以是使用名称和/或其他反射属性的嵌套事件注册。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-04
    • 2023-03-12
    • 2014-07-10
    • 2017-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多