Как заставить только самого дальнего ребенка получить общее событие? - PullRequest
3 голосов
/ 30 марта 2012

Я столкнулся с небольшой проблемой дизайна моего кода. У меня есть объект, который создает дочерний объект (потом ребенок может создать другого дочернего объекта и т. Д.), И оба объекта подписываются на одно и то же событие. Но я хочу, чтобы событие получал только самый дочерний объект.

Обзор моего проекта: Я создаю систему IVR. Когда пользователь звонит в систему, у него будет выбор из X меню. В зависимости от того, что выберет пользователь, у него будет подменю выбора, и так далее, и так далее. Я использую государственные машины для этого. Каждый конечный автомат должен «слушать», когда пользователь нажимает номер на своем телефоне. Но только текущий конечный автомат должен обработать введенный номер. Каждый конечный автомат может создать новый конечный автомат для представления подменю.

Вот пример кода:

Базовый класс:

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

Objecta:

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

ObjectB:

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, поэтому мне нужна реализация на базовом уровне, а не на уровне 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 ]

2 голосов
/ 30 марта 2012

вам нужно будет явно реализовать обработчики myEvent (добавить / удалить) и отследить «дальний» независимо от зарегистрированных наблюдателей.затем вы можете отправить уведомление этому единственному экземпляру.

1 голос
/ 30 марта 2012

Для событий каждый подписчик ставится в очередь (помещается в конец списка), модель 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;
    }
  }

На этом этапе вы можете проверить Handled на каждом IObject

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

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

Или добавьте некоторую сложность вашему Базовому классу и прекратите вызывать цепочку (давайте детям не нужно проверять Handled). Я покажу решение с помощью List <> делегатов, но некоторые приведения и вызовы MulticaseDelegate могут делать то же самое. Я просто чувствую, что код List <> может быть более читабельным / поддерживаемым.

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;
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...