区别委托和事件

上一篇

对不熟悉 .NET Core 平台的开发人员而言,在基于 delegates 的设计和基于 events 的设计之间做出选择是困难的。 委托或事件的选择通常比较难,因为这两种语言功能很相似。 事件甚至是使用委托的语言支持构建的。

它们都提供了一个后期绑定方案:在该方案中,组件通过调用仅在运行时识别的方法进行通信。 它们都支持单个和多个订阅服务器方法。 这称为单播和多播支持。 二者均支持用于添加和删除处理程序的类似语法。 最后,引发事件和调用委托使用完全相同的方法调用语法。 它们甚至都支持与 ?. 运算符一起使用的相同的 Invoke() 方法语法。

鉴于所有这些相似之处,很难确定何时使用何种语法。

侦听事件是可选的

在确定要使用的语言功能时,最重要的考虑因素为是否必须具有附加的订阅服务器。 如果代码必须调用订阅服务器提供的代码,则在需要实现回调时,应使用基于委托的设计。 如果你的代码在不调用任何订阅服务器的情况下可完成其所有工作,则应使用基于事件的设计。

请考虑本部分中生成的示例。 必须为使用 List.Sort() 生成的代码提供 comparer 函数,以便对元素进行正确排序。 必须与委托一起提供 LINQ 查询,以便确定要返回的元素。 二者均使用与委托一起生成的设计。

请考虑 Progress 事件。 它会报告任务进度。 无论是否具有侦听器,该任务将继续进行。 FileSearcher 是另一个示例。 即使没有附加事件订阅服务器,它仍将搜索和查找已找到的所有文件。 即使没有任何订阅服务器侦听事件,UX 控件仍正常工作。 它们都使用基于事件的设计。

返回值需要委托

另一个注意事项是委托方法所需的方法原型。 如你所见,用于事件的委托均具有无效的返回类型。 你还看到,存在创建事件处理程序的惯用语,该事件处理程序通过修改事件参数对象的属性将信息传回到事件源。 虽然这些惯用语可发挥作用,但它们不像从方法返回值那样自然。

请注意,这两种试探法可能经常同时存在:如果委托方法返回值,则可能会以某种方式影响算法。

事件具有专用调用

包含事件的类以外的类只能添加和删除事件侦听器;只有包含事件的类才能调用事件。 事件通常是公共类成员。 相比之下,委托通常作为参数传递,并存储为私有类成员(如果它们全部存储)。

事件侦听器通常具有较长的生存期

事件侦听器通常具有较长的生存期的这一理由不太充分。 但是,你可能会发现,当事件源将在很长一段时间内引发事件时,基于事件的设计会更加自然。 可以在许多系统上看到基于事件的 UX 控件设计示例。 订阅事件后,事件源可能会在程序的整个生存期内引发事件。 (当不再需要事件时,可以取消订阅事件。)

将其与许多基于委托的设计(其中委托用作方法的参数,且在返回该方法后不再使用此委托)进行比较。

仔细评估

以上考虑因素并非固定不变的规则。 相反,它们代表可帮助决定针对特定使用情况的最佳选择的指南。 因为两者类似,所以甚至可以将两者作为原型,并考虑使用更加自然的一种。 两者均能很好地处理后期绑定方案。 使用能与设计进行最佳通讯的一种。