Почему обработчики событий всегда имеют возвращаемый тип void? - PullRequest
24 голосов
/ 24 июля 2010

Эй, я удивляюсь, почему возвращаемый тип событий, таких как

private void button1_Click(object sender, EventArgs e)

, всегда недействителен?

Может ли он возвращать и любое другое значение?

Ответы [ 10 ]

33 голосов
/ 24 июля 2010

Подпись обработчика события, то есть тип возвращаемого значения, а также количество и типы принимаемых им аргументов, определяется подписью delegate, используемой для определения события.Таким образом, событие Click для Button в вашем примере не поддерживает никаких возвращаемых значений.

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

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

16 голосов
/ 24 июля 2010

Событие может иметь возвращаемое значение.Но это руководство BCL, чтобы возвращать пустоту (и иметь 2 параметра).

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

Еще одна практика BCL - обмениваться и возвращать информацию через свойство записи для наследника EventArgs, например, свойство Cancel события Window.Closing.Все обработчики могут видеть и изменять это.Все еще решение последней победы, но лучше.

Но, сказав все это, вы все равно могли бы написать:

delegate int Summer(int[] arr);  // delegate

class Program
{
    public event Summer OnSum;   // event

    void DoSum()
    {
        int[] data = {1, 2, 3} ;              
        int sum = 0;

        if (OnSum != null)  
          sum = OnSum(data);   // execute it.
    }
}
9 голосов
/ 24 июля 2010

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

Этопросто для обработчиков событий не имеет смысла возвращать значение.Как правило, они изменяют состояние некоторого объекта, полученного из EventArgs, чтобы сообщить что-то обратно тому, что вызвало событие.

5 голосов
/ 23 мая 2013

В c # события могут быть двух типов

1. Multicast
2. UnitCast

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

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

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

public delegate int buttonClick;

public event buttonClick onClick;

onClick += method1
onclick += method2
onclick += metho3

когда это событие будет вызвано, тогда значение, возвращаемое method1, будет заменено значением, возвращаемым method2, и в итоге будет получено значение только method3.

Следовательно, в случае делегата многоадресной рассылки всегда рекомендуется не возвращать никакого значения.

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

Итак, для многоадресного делегата - тип возврата отсутствует а для одноадресного делегата - может иметь тип возврата

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

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

var handler = _eventToRaised.GetInvocationList();
foreach(var handler in handlers)
{
  if(handler != null)
   {
    var returnValue = handler()// pass the values which delegate expects.
   }

}
5 голосов
/ 24 июля 2010

Конечно, события могут возвращать значения.

   [TestClass]
   public class UnitTest1 {
      delegate int EventWithReturnValue();

      class A {
         public event EventWithReturnValue SomeEvent;
         public int LastEventResult { get; set; }

         public void RaiseEvent() {
            LastEventResult = SomeEvent();
         }
      }

      [TestMethod]
      public void TestMethod1() {
         A a = new A();
         a.SomeEvent += new EventWithReturnValue(a_SomeEvent);
         a.RaiseEvent();
         Assert.AreEqual(123, a.LastEventResult);
      }

      int a_SomeEvent() {
         return 123;
      }
   }

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

5 голосов
/ 24 июля 2010

Вы не можете сделать это, потому что делегат, который обрабатывает событие, ожидает определенной подписи (вы получите ошибку компиляции, если попытаетесь изменить ее).Например, делегат в этом случае (Button.Click) является System.EventHandler, он должен соответствовать этой подписи, чтобы компилировать / функционировать как делегат вообще:

public delegate void EventHandler(Object sender, EventArgs e)

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

MyButton.Click += button1_Click;

Если вы вернули что-нибудь еще ...это будет использоваться для?Если вы намереваетесь вызвать что-то, что возвращает результат ... для этого нужен метод, а не EventHandler:)

3 голосов
/ 24 июля 2010

Поскольку многие люди уже заявили, что это соглашение, а не ограничение. Вы можете иметь событие, возвращающее вещи в самом EventArgs. Microsoft использует этот шаблон во многих местах, см. Событие FormClosing в WinForms. Поэтому, если вы хотите вернуть значение, сделайте что-то вроде следующего:

public class AllowCloseEventArgs : EventArgs
{
    public bool AllowClose = true;
}

public void AllowClose(object sender, AllowCloseEventArgs e)
{ e.AllowClose = false; }

Теперь, зная это, давайте обсудим, почему дизайнеры выбрали «стандартный» прототип событий void-return:

  1. Если бы у них было возвращаемое значение, какой бы это был тип?
  2. Если событие вернуло значение, что бы это значение значило?
  3. Не имея возвращаемого типа, события могут быть однонаправленными. То есть, если меня не волнуют исключения, я могу «запустить и забыть» событие, например, вызвать Control.BeginInvoke (...)

Обновление: Бен справедливо добавляет: # 4: что если событию нужно вернуть более одного значения?

3 голосов
/ 24 июля 2010

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

public class AnEvent
{
  public delegate MyReturnType MyDelegateName();
  public event MyDelegateName MyEvent;

  public void DoStuff()
  {
    MyReturnType result = null;
    if (MyEvent != null)
      result = MyEvent();
    Console.WriteLine("the event was fired");
    if (result != null)
      Console.Writeline("the result is" + result.ToString());
  }
}

public class EventListener
{
  public EventListener()
  {
    var anEvent = new AnEvent();
    anEvent.MyEvent += SomeMethod;
  }

  public MyReturnType SomeMethod()
  {
    Console.Writeline("the event was handled!");
    return new MyReturnType;
  }
}
1 голос
/ 24 июля 2010

Тип возвращаемого значения void, потому что это подпрограмма, а не функция.Вы могли бы получить его, чтобы вернуть значение, но обработчики событий (то есть то, чем является подпрограмма, подключенная к событию нажатия кнопки) не совсем предназначены.

В VB эта строкакода будет:

Private Sub button_Click(ByVal sender As Object, ByVal e As EventArgs)

Явный оператор "Sub" в VB имеет немного больше смысла в этом случае, но помните, что все void в C # - просто подпрограммы ... они делают что-тов коде, основанном на аргументах, но не возвращает значение.Однако они могут изменять значения передаваемых аргументов.

0 голосов
/ 24 июля 2010

Как говорил мой профессор в колледже, почти на каждый вопрос "Это зависит от реализации".В этом конкретном случае, работая с обработчиками событий, в этой модели нет ничего неявного, что могло бы помешать реализации этого шаблона и возвращению чего-либо обратно в вызывающий код.Однако вы передаете как объект-отправитель, так и исходные аргументы события, в основном формируя контекст выполняемой среды, нет необходимости возвращать что-либо, поскольку вы можете напрямую работать с этими ссылками для достижения любой соответствующей функциональности.

В других средах обработчик событий может что-то возвращать.

...