Является ли это плохой формой для класса С# для подписки на собственные опубликованные события?

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

Это звучит разумно для меня, но я не могу удержаться от ощущения, что это плохая практика по той простой причине, что я всегда сталкиваюсь с вопросом: "Почему бы не выполнить действия, которые вы предоставили бы в обработчик события в коде, который запускает событие?"

public class Button
{
   public Button()
   {
      this.Click += someHandler; // bad practice?
   }

   public event EventHandler Click;

   public void HandleInput()
   {
      if (someInputCondition)
      {
         // Perform necessary actions here rather than 
         // subscribing in the constructor?
         this.Click(this, ...);
      }
   }
}

Есть ли недостатки для подписки на ваши собственные события?

Ответ 1

Это звучит разумно для меня, но я не могу удержаться от ощущения, что это плохая практика по той простой причине, что я всегда сталкиваюсь с вопросом: "Почему бы не выполнить действия, которые вы предоставили бы в обработчик события в коде, который запускает событие?"

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

В этом случае представляется вполне разумным, что пользовательская сторона D хотела бы зарегистрироваться, чтобы быть вызванной, когда события, объявленные B или машинная сторона D, были подняты машинной стороной D.

Это был сценарий, в котором мы оказались при разработке VSTO много лет назад. Как оказалось, это не сложно было сделать на С#, но было довольно сложно заставить все это работать в VB. Я считаю, что VB сделал некоторые изменения в своей модели подписки на события, чтобы сделать это проще.

Это сказало: если вы можете избежать этого, я бы это сделал. Если вы просто делаете событие для внутренней подписки, которое кажется плохим запахом кода. Частичные методы в С# 3 очень помогают в этом, так как они упрощают и недорогу для создаваемой машиной стороны, чтобы вызвать небольшие функции уведомлений в пользовательской части, не прибегая к проблеме публикации события.

Ответ 2

Я не вижу проблемы с этим. Но если вы обрабатываете события в одном классе, вы также можете переопределить метод события:

protected override void OnClick(Eventargs e)
{
   base.OnClick(e);
}

Это более эффективно и дает вам возможность усвоить событие в случае необходимости (просто не вызывая base.OnClick()).

Ответ 3

Там очень экзотическая проблема из-за внутренней оптимизации при этом. Из-за оптимизации добавления/удаления обработчики событий не являются потокобезопасными. Он применяется только к событиям, которые используются типом объявления, как в вашем примере.

К счастью, это было изменено с 4.0, но если вы находитесь в предыдущей версии, вы можете испытать это.

Ответ 4

Проблема заключается в том, что "someHandler" изменит состояние вашего объекта. Вы хотите, чтобы это состояние менялось до или после какого-либо "внешнего" кода, выполняемого событием?

Неясно, в какой момент произойдет изменение состояния, если вы подписаны на событие, однако вызов его в "HandleInput()" делает его намного понятнее, когда он будет вызван.

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

Сказав вышеизложенное, обычно не наносит большого вреда при подписке на ваше собственное событие; в классах пользовательского интерфейса, которые представляют формы, они очень распространены, в противном случае они склонны "удивлять" многих людей, которые читают ваш код.

Ответ 5

Если ваш класс кнопки должен быть первым, который получает событие click, вы должны написать свой код в методе событий, например.

protected virtual void OnClick(EventArgs e)
{
    //insert your code here

    if(this.Click != null)
    {
        this.Click(this, e);
    }
}

но если вам не нужно, чтобы ваш класс был первым приемником, вы можете подписаться на это событие нормально.

Ответ 6

если вы возьмете обычный класс System.Windows.Form в качестве примера,
 когда вы хотите обработать событие Form_Load (используя визуальный дизайнер студии), он обрабатывается в классе самой Формы!

this.Load += new System.EventHandler(this.Form1_Load);

 private void Form1_Load(object sender, EventArgs e)
 {
 }

поэтому я думаю, что это не проблема вообще.