如何让只有最远的孩子接收共享事件?

时间:2012-03-30 19:09:44

标签: c# events

我的代码遇到了一些设计问题。 我有一个创建子对象的对象(子进程可以创建另一个子对等),并且这两个对象都订阅同一个事件。 但是,我只希望最多的子对象接收事件。

我的项目概述: 我正在创建一个IVR系统。当用户呼入系统时,用户将有X菜单选项。根据用户选择的内容,他们将有一个选择子菜单,依此类推。我正在使用状态机。当用户按下手机上的号码时,每台国家机器都需要“收听”。但只有当前的State Machine需要处理输入的数字。每个状态机都可以创建一个新的状态机来表示子菜单。

以下是一些示例代码:

基类:

public delegate void DoSomething(object sender, EventArgs data);
public class Base
{
    public event DoSomething myEvent;
    private IObject foo;

    public Base ()
    {
        foo = new myObjectA(this);
    }

    public void SomeAction()
    {
        ((myObjectA)foo).CreateChild();
    }

    public void EventFired()
    {
        if (myEvent != null)
        {
            myEvent(this, new EventArgs());
        }
    }
}

对象A:

class myObjectA : IObject
{
    private Base theCallingObject;
    private IObject child;
    public myObjectA (Base _base)
    {
        theCallingObject = _base;
        theCallingObject.myEvent += new DoSomething(theCallingObject_myEvent);
    }

    public void CreateChild()
    {
        child = new myObjectB(theCallingObject);
    }

    void theCallingObject_myEvent(object sender, EventArgs data)
    {
        // Handle event
        MessageBox.Show("myObjectA");
    }
}

对象B:

class myObjectB : IObject
{
    private Base theCallingObject;
    public myObjectB (Base _base)
    {
        theCallingObject = _base;
        theCallingObject.myEvent += new DoSomething(theCallingObject_myEvent);
    }

    void theCallingObject_myEvent(object sender, EventArgs data)
    {
        // Handle event
        MessageBox.Show("myObjectB");
    }
}

现在我这样做了:

Base blah = new Base();
blah.SomeAction();
blah.EventFired();

我收到了A和B的消息框。 我需要实现Base,以便只有myObjectB才能获取事件。 我将有数百个myObject,所以我需要在Base级别而不是myObject级别实现。另外,在myObject级别处理它仍然需要触发事件,如果有数百个对象,则会导致性能问题。

我考虑过的一个解决方案是myObjectA创建子项时,取消订阅事件,然后在我们返回myObjectA级别时重新订阅。但是我觉得可以做得更好。

有人有什么想法吗?

编辑:使用payo的输入我想出了这个:

public delegate void DoSomething(object sender, EventArgs data);
public class Base
{
    private IObject foo;
    private List<DoSomething> _myEventStorage;

    public event DoSomething myEvent
    {
        add
        {
            _myEventStorage.Insert(0, value);
        }
        remove
        {
            _myEventStorage.Remove(value);
        }
    }

    public Base ()
    {
        _myEventStorage = new List<DoSomething>();
        foo = new myObjectA(this);
    }

    public void SomeAction()
    {
        ((myObjectA)foo).CreateChild();
    }

    public void EventFired()
    {
        _myEventStorage[0].Invoke(this, new EventArgs());
    }
}

2 个答案:

答案 0 :(得分:2)

您需要显式实现myEvent(添加/删除)处理程序并跟踪已注册观察者的“最远”。然后您可以将通知发送到该单个实例。

答案 1 :(得分:1)

对于事件,每个订户排队(放在列表末尾),一个FIFO模型。您希望最多子对象“拥有”该事件,而不仅仅是订阅并成为其他未知对象的一些抽象列表的一部分。

我会提供一个代表你想要做的事情的新模型。这可能是杰森推荐的:(当我输入时,他发布了答案)

public class Base
{
  private DoSomething _myEventStorage;
  public event DoSomething myEvent
  {
    add
    {
      _myEventStorage = value;
    }
    remove
    {
      _myEventStorage -= value;
    }
  }
...
  public void EventFired()
  {
    if (_myEventStorage != null)
    {
      _myEventStorage(this, new ChainEventArgs());
    }
  }
}

这只是最后一次。另一个选项(添加到此自定义添加/删除)将提供派生的EventArgs:

public class ChainEventArgs : EventArgs
{
  public bool Handled { get; set; }
}
public delegate void DoSomething(object sender, ChainEventArgs data);

...

  public event DoSomething myEvent
  {
    add
    {
      var temp = _myEventStorage;
      _myEventStorage = null;
      _myEventStorage += value;
      _myEventStorage += temp; // now all are called, but FILO
    }
    remove
    {
      _myEventStorage -= value;
    }
  }

此时,您可以检查每个IObject上的Handled

void theCallingObject_myEvent(object sender, ChainEventArgs data)
{
  if (data.Handled)
    return;

  if (I_want_to_block_parents)
    data.Handled = true;
  // else leave it false
}

或者,为您的Base类添加一些复杂性并停止调用链(让孩子们不需要检查Handled)。我将使用List&lt;&gt;来显示解决方案代表,但一些MulticaseDelegate演员和调用可以做同样的事情。我只是觉得List&lt;&gt;代码可能更具可读性/可维护性。

public class Base
{
  private List<DoSomething> _myEventStorage;
  public event DoSomething myEvent
  {
    add
    {
      _myEventStorage.Insert(0, value);
    }
    remove
    {
      _myEventStorage.Remove(value);
    }
  }
...
  public void EventFired()
  {
    var args = new ChainEventArgs();
    foreach (var handler in _myEventStorage)
    {
      handler(this, args);
      if (args.Handled)
        break;
    }
  }
}