что лучший способ состоит в том, чтобы реализовать объединенные в цепочку события в C#

Я решил "нажатие на клавишу Return в причинах подчиненной формы входа в систему основная форма для представления" проблемы в моем текущем проекте путем встраивания iframe в основную страницу. iframe указал на login.aspx страницу, которая аутентифицировала пользователя.

<iframe id="login" src="login.aspx" frameborder="0" enableviewstate="false" scrolling="no" runat="server"></iframe>

(формируют некоторую причину, мне было нужно закрытие,/iframe отмечают запутанный в других отношениях режим конструктора)

6
задан Vinko Vrsalovic 12 July 2009 в 08:08
поделиться

5 ответов

Если это кажется неправильным, потому что события более сложны и обращены вовне, чем это необходимо для вашей внутренней коммуникации (что я считаю, по крайней мере, частично верно, учитывая, что события могут вызывать несколько клиентов, тогда как вы знаете, что вам нужно только чтобы уведомить одного, не так ли?), то я предлагаю следующий вариант. Вместо использования событий для сообщения о завершении 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 является частным, или использовать механизм, подобный предложенному Павлом. Я думаю, однако, что клиентам нравится простота реализации меньшего количества обработчиков событий и различение источника в одном обработчике, вместо того, чтобы подключать (и потенциально отключать) события от десятков более мелких объектов.

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

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

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

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

4
ответ дан 9 December 2019 в 22:38
поделиться

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

Ссылки:

3
ответ дан 9 December 2019 в 22:38
поделиться

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

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

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;

Действительно, Решарпер может даже предложить вам это! Но это означает совсем другое.

2
ответ дан 9 December 2019 в 22:38
поделиться

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

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

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

Фасадные классы и общие принципы сокрытия информации, однако, предназначены для облегчения исполнения контрактов.

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

1
ответ дан 9 December 2019 в 22:38
поделиться

Вы можете делегировать 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
ответ дан 9 December 2019 в 22:38
поделиться
Другие вопросы по тегам:

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