为什么事件处理程序总是有返回类型的void?

嘿,我想知道为什么返回类型的事件如

private void button1_Click(object sender, EventArgs e) 

永远无效?

它还可以返回任何其他值吗?

事件处理程序签名,即返回类型及其所采用的参数的数量和类型,由用于定义事件的delegate的签名确定。 因此,示例中Button的Click事件不支持任何返回值。

通常,您不希望从事件处理程序返回值作为函数返回值,因为事件可以有多个订阅者,并且每个订阅者都将独立于其他处理程序返回返回值,并且需要特殊事件触发代码来决定处理所有返回值。

通常,如果您需要从事件处理程序返回通信,则EventArgs结构将包含处理程序可以更新的成员,并且每个处理程序将获得查看值并相应更新的机制,并且触发事件的代码只需要响应结构中的最终值。

事件可以具有返回值。 但它是返回void的BCL指南(并且有2个参数)。

使用事件的多播属性时,返回值会变得有点混乱。 返回的值是最后执行的处理程序的值。 所有其他订阅处理程序的返回都将丢失,并且由于事件用于解耦,因此您无法控制它们的执行顺序。 这使得返回值非常不切实际。

通过EventArgs后代的可写属性共享和返回信息是另一种BCL实践,例如Window.Closing事件的Cancel属性。 所有处理程序都可以查看和更改此内容。 仍然是最后一胜的解决方案,但更好。

但说了这么多,你仍然可以写:

 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. } } 

除了.NET期望标准控件中事件的某个签名这一事实之外,请考虑以下事项:事件可以附加多个事件处理程序,其中一个返回值将被使用?

事件处理程序返回值只是没有意义。 通常,它们将修改从EventArgs派生的某个对象的状态,以便将某些内容传回给触发事件的内容。

你不能这样做,因为处理事件的委托期望某个签名(如果你尝试更改它,你会得到编译错误)。 例如,在这种情况下( Button.Click )的委托是一个System.EventHandler ,它必须匹配该签名以编译/作为委托作用:

 public delegate void EventHandler(Object sender, EventArgs e) 

这就是代表的工作方式,当你看看它通常如何使用时,它更有意义:

 MyButton.Click += button1_Click; 

如果你还有其他任何东西……它将用于什么? 如果你打算调用返回结果的东西……这就是方法的用途,而不是EventHandler 🙂

当然,事件可以返回值。

  [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; } } 

但是,使用事件返回值来交换组件与其消费者之间的信息并不常见。

在c#中,Events可以有两种类型

1.组播
2. UnitCast

多播事件是指具有多个订户的事件。 当引发多播事件时,将调用多个处理程序,因为您已订阅了多个事件处理程序。 那么,多播事件是用来调用多个处理程序而多播处理程序不能有返回类型为什么?

因为如果多播委托具有返回类型,则每个事件处理程序都将返回一些值,并且一个事件处理程序的返回值将被下一个事件处理程序值替换。

假设您有一个多播委托如下

 public delegate int buttonClick; public event buttonClick onClick; onClick += method1 onclick += method2 onclick += metho3 

当这个事件被引发时,method1返回的值将被method2返回的值替换,最终只接收method3的值。

因此,在Multicast委托的情况下,始终建议不要返回任何值。

但是在unicast deleagte的情况下,你只有一个用户。 因此,您可以返回价值以实现您的目的

因此,对于多播委托 – 无返回类型和单播委托 – 可以具有返回类型

多播委托也可以返回多个值,但必须手动引发该事件。

如果你选择多播委托也应该返回值的事件,假设我有一个绑定到4事件处理程序的事件,它需要两个整数,一个处理程序添加,第二个减法和第三个乘法和最后一个除法。 因此,如果要获取所有处理程序的返回类型,则必须以下列方式手动引发事件,

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

默认的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; } } 

正如许多人已经说过这一公约而不是约束。 您可以在EventArgs本身内返回事件。 Microsoft在许多地方使用此模式,请参阅WinForms中的FormClosing事件。 因此,如果要返回值,请执行以下操作:

 public class AllowCloseEventArgs : EventArgs { public bool AllowClose = true; } public void AllowClose(object sender, AllowCloseEventArgs e) { e.AllowClose = false; } 

现在了解这一点让我们讨论为什么设计师选择“标准”的void-return事件原型:

  1. 如果他们有一个返回值,它会是什么类型?
  2. 如果事件返​​回一个值,该值意味着什么?
  3. 由于没有返回类型,事件可以是单向的。 意思是如果我不关心exception,我可以“触发并忘记”事件,如调用Control.BeginInvoke(…)

更新:Ben正确地补充说:#4:如果事件需要返回多个值,该怎么办?

返回类型为void,因为它是子例程,而不是函数。 你可以让它返回一个值,但事件处理程序(这是一个挂钩到按钮单击事件的子例程)并不是完全有意的。

在VB中,这行代码将是:

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

在这种情况下,VB中的显式“Sub”语句更有意义,但请记住,C#中的所有void都只是子例程……它们在代码中基于参数执行某些操作但不返回值。 但是,它们可以更改传入的参数的值。

作为我的大学教授过去几乎每个问题都说“这取决于实施”。 在这种特殊情况下,使用事件处理程序时,此模型没有任何隐含的内容可以阻止和实现此模式以将某些内容返回给调用代码。 但是,您既传递了sender对象又传递了原始事件参数,基本上形成了执行环境上下文,不需要返回任何内容,因为您可以直接使用这些引用来实现任何相关function。

其他框架可能允许事件处理程序返回一些东西。