Вопрос по c#, events, .net – Каков наилучший способ реализации связанных событий в C #

6

У меня есть следующий сценарий. Код клиента имеет доступ только к FooHandler, но не напрямую к экземплярам Foo.

public delegate void FooLoaded(object sender, EventArgs e);

class Foo {
    public event FooLoaded loaded;
    /* ... some code ... */
    public void Load() { load_asynchronously(); }
    public void callMeWhenLoadingIsDone() { loaded(this,EventArgs.Empty); }
}

class FooHandler {
    public event FooLoaded OneFooLoaded;

    /* ... some code ... */

    public void LoadAllFoos() { 
        foreach (Foo f in FooList) { 
            f.loaded += new FooLoaded(foo_loaded);
            f.Load(); 
        } 
    }

    void foo_loaded(object sender, EventArgs e) {
        OneFooLoaded(this, e);
    }

}

Затем клиенты будут использовать событие OneFooLoaded класса FooHandler для получения уведомлений о загрузке foos. Является ли это «цепочкой событий»? что нужно сделать? Есть ли альтернативы? Мне это не нравится (кажется неправильным, я не могу точно объяснить, почему), но если я хочу, чтобы обработчик был точкой доступа, у меня, похоже, нет многих альтернатив.

Ваш Ответ

5   ответов
2

Напишите объявление события следующим образом:

public event FooLoaded loaded = delegate {};

Таким образом, вы можете безопасно запустить его, даже если клиенты не зачислены.

На тему цепочки событий, когда у вас есть два события:

public event EventHandler a = delegate {};
public event EventHandler b = delegate {};

Вы можете захотеть, чтобы запуск b также вызывал запуск a:

b += (s, e) => a(s, e);

И тогда вы можете посмотреть на это и подумать, что было бы более кратким сказать:

b += a;

Действительно, Решарпер может даже предложить вам это! Но это означает что-то совершенно другое. Добавляетcurrent contents изa вbтак что если позже, то больше обработчиков подключатся сa, это не приведет к их вызову, когдаb уволен.

4

потому что события более сложные и внешние, чем это необходимо для ваших внутренних коммуникаций (что, я считаю, по крайней мере частично верно, учитывая, что события могут вызывать несколько клиентов, тогда как вы знаете, что вам нужно только уведомить одного, верно?), Тогда я предложить следующую альтернативу. Вместо использования событий для передачи завершения Foo в FooHandler, поскольку Foo в любом случае является внутренним, вы можете добавить параметр обратного вызова в конструктор или метод Load Foo, который Foo может вызывать после завершения загрузки. Этот параметр может быть просто функцией, если у вас есть только один обратный вызов, или это может быть интерфейс, если у вас их много. Вот как я думаю, что ваш код будет выглядеть с упрощенным внутренним интерфейсом:

public delegate void FooLoaded(FooHandler sender, EventArgs e);

class Foo
{
  Action<Foo> callback;
  /* ... some code ... */
  public void Load(Action<Foo> callback) { this.callback = callback; load_asynchronously(); }
  public void callMeWhenLoadingIsDone() { callback(this); }
}

class FooHandler
{
  public event FooLoaded OneFooLoaded;

  /* ... some code ... */

  public void LoadAllFoos()
  {
     foreach (Foo f in FooList)
     {
        f.Load(foo_loaded);
     }
  }

  void foo_loaded(Foo foo)
  {
     // Create EventArgs based on values from foo if necessary
     OneFooLoaded(this, null);
  }

}

Обратите внимание, что это также позволяет более строго типизировать с делегатом FooLoaded.

Если, с другой стороны, он чувствует себя неправильно, потому что событие не должно проходить через FooHandler, чтобы добраться до клиента, то 1) я бы оспорил это, потому что, если клиент не хочет иметь дело с отдельными объектами Foo, он также не должен получать события от них на этом уровне, и 2) Если вы действительно хотите это сделать, вы можете реализовать некоторый публичный интерфейс обратного вызова в Foo, даже если Foo является закрытым, или использовать механизм, подобный предложенному Павлом. Я думаю, однако, что клиентам нравится простота реализации меньшего числа обработчиков событий и различения источника внутри одного обработчика, а не необходимость подключать (и потенциально отключать) события от десятков меньших объектов.

Хотелось бы, чтобы я видел это до начала проекта, над которым я сейчас работаю, это сделало все для меня намного проще (и намного проще для модульного тестирования).
3

где проходят все события. Любые классы, использующие домен, будут подключаться к этому классу, который имеет список статических событий, и любое внутреннее событие класса в домене будет прослушиваться этим классом, тем самым избегая цепочки событий в домене, по крайней мере.

Рекомендации:

Udi Dahan with code examples on domain events Martin Fowler more general on domain events
Я только что нашел это и думаю, что это фантастика, намного проще, чем пытаться соединить 2 или 3 события и получить спагетти
1

add а такжеremove на событие вместо повышения:

class FooHandler {
    public event FooLoaded OneFooLoaded {
       add { 
           foreach (Foo f in FooList) {
               f.loaded += new FooLoaded(value);
           }
       }
       remove {
           foreach (Foo f in FooList) {
               f.loaded -= new FooLoaded(value);
           }
       }
    }

    public void LoadAllFoos() { 
        foreach (Foo f in FooList) { 
            f.Load(); 
        } 
    }
}

Вышесказанное предполагает, чтоFooList неизменен на протяжении всей жизниFooHandler, Если он изменчив, то вам также придется отслеживать добавление / удаление элементов в нем и соответственно добавлять / удалять обработчики.

1

event waterfall это то, к чему я довольно естественно пришел несколько раз, и мне еще предстоит столкнуться с серьезной проблемой с ними.

Хотя я и не думаю, что когда-либо передавал события прозрачно, но всегда с семантическим изменением.FooLoaded станетAllFoosLoaded, например. Если вы хотите навязать такое семантическое изменение просто ради него, вы можете изменитьOneFooLoaded к показателю в процентах (должен ли получающий класс знать, сколькоFoos загружены?), например.

Я думаю, что такие конструкции кажутся неправильными, потому чтоevent предназначен для трансляции. Он на самом деле не навязывает контракт классу, который его передает, и не налагает контракт на класс, подписывающийся на него.

Тем не менее, классы Fa &dea и общие принципы сокрытия информации призваны облегчить выполнение контрактов.

Я все еще собираю свои мысли по этому вопросу, извините, если вышеизложенное немного неясно, но я не знаю, есть ли лучший способ сделать то, что вы хотите. И если это так, мне так же интересно видеть его, как и вы.

Похожие вопросы