.NET EventHandlers-通用还是不通用?
每次我深入Cproject时,都会遇到很多事件,这些事件实际上只需要传递一个项目即可。我坚持使用EventHandler
/EventArgs
的做法,但是我喜欢做的事情是这样的:
public delegate void EventHandler<T>(object src, EventArgs<T> args); public class EventArgs<T>: EventArgs { private T item; public EventArgs(T item) { this.item = item; } public T Item { get { return item; } } }
以后我可以拿我的
public event EventHandler<Foo> FooChanged; public event EventHandler<Bar> BarChanged;
但是,.NET的标准似乎是为每种事件类型创建一个新的委托和EventArgs子类。我的通用方法有什么问题吗?
编辑:这篇文章的原因是,我只是在一个新项目中重新创建了这个,并且想确保它没问题。实际上,我在发布时正在重新创建它。我发现有一个通用的EventHandler <TEventArgs>,因此我们不需要创建通用的委托,但是仍然需要通用的EventArgs <T>类,因为TEventArgs:EventArgs。
另一个编辑:内置解决方案的一个缺点(对我而言)是多余的:
public event EventHandler<EventArgs<Foo>> FooChanged;
与
public event EventHandler<Foo> FooChanged;
但是,对于客户端来说,注册事件可能会很痛苦,因为默认情况下会导入System名称空间,因此即使使用像Resharper这样的高级工具,他们也必须手动查找名称空间。 ?
解决方案
不,我认为这不是错误的方法。我认为甚至在[出色]的《框架设计指南》中也推荐使用。我做同样的事情。
我确实相信,.NET的最新版本在其中定义了这样的事件处理程序。就我而言,这是个大赞。
/编辑
本来没有区别。只要我们返回的是从EventArgs继承的类,我就看不到问题。如果我们出于可维护性的原因没有包装结果,我会很担心。我仍然说这对我来说看起来不错。
这是正确的实现。自从泛型(2.0)首次面世以来,它已被添加到.NET Framework(mscorlib)中。
有关其用法和实现的更多信息,请参见MSDN:http://msdn.microsoft.com/zh-cn/library/db0etb8x.aspx
从.NET 2.0开始
EventHandler<T>
已实施。
第一次看到这个小模式时,我使用的是MS Patterns&Practices组的Composite UI Application块。
它不会向我发出任何危险信号;实际上,这甚至是利用泛型遵循DRY规则的明智方法。
我们可以在MSDN上找到通用EventHandler,网址为http://msdn.microsoft.com/zh-cn/library/db0etb8x.aspx
我已经广泛使用通用EventHandler并能够防止所谓的"类型爆炸(类)"
项目保持较小,更易于浏览。
为非通用EventHandler委托想出一个新的直观委托,这很痛苦并且与现有类型重叠
在我看来,将" * EventHandler"添加到新的委托人名称并没有太大帮助
自.NET Framework 2.0以来,已添加以下形式的代表
public delegate void EventHandler<TArgs>(object sender, TArgs args) where TArgs : EventArgs
因为我们为EventArgs提供了具有单个数据项的现成实现,所以方法要走得更远,但是它缺少原始想法的多个属性:
- 我们不能在不更改相关代码的情况下向事件数据添加更多属性。我们将必须更改委托签名以向事件订阅者提供更多数据。
- 数据对象是通用的,但它也是"匿名的",在阅读代码时,我们将必须从用法中解密" Item"属性。应该根据它提供的数据来命名。
- 当具有基础(项目)类型的层次结构时,以这种方式使用泛型,就无法使EventArgs成为并行层次结构。例如。即使BaseType是DerivedType的基础,EventArgs <BaseType>也不是EventArgs <DerivedType>的基础类型。
因此,我认为最好使用通用EventHandler <T>,但仍然具有根据数据模型的要求进行组织的自定义EventArgs类。使用Visual Studio和诸如ReSharper之类的扩展,只需几个命令即可创建类似的新类。
为了简化通用事件声明,我为其创建了两个代码段。要使用它们:
- 复制整个代码段。
- 将其粘贴到文本文件中(例如在记事本中)。
- 使用.snippet扩展名保存文件。
- 将.snippet文件放入我们合适的代码段目录中,例如:
Visual Studio 2008 \代码段\ Visual C#\我的代码段
这是一个使用带有一个属性的自定义EventArgs类的示例:
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Generic event with one type/argument.</Title> <Shortcut>ev1Generic</Shortcut> <Description>Code snippet for event handler and On method</Description> <Author>Kyralessa</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>type</ID> <ToolTip>Type of the property in the EventArgs subclass.</ToolTip> <Default>propertyType</Default> </Literal> <Literal> <ID>argName</ID> <ToolTip>Name of the argument in the EventArgs subclass constructor.</ToolTip> <Default>propertyName</Default> </Literal> <Literal> <ID>propertyName</ID> <ToolTip>Name of the property in the EventArgs subclass.</ToolTip> <Default>PropertyName</Default> </Literal> <Literal> <ID>eventName</ID> <ToolTip>Name of the event</ToolTip> <Default>NameOfEvent</Default> </Literal> </Declarations> <Code Language="CSharp"><![CDATA[public class $eventName$EventArgs : System.EventArgs { public $eventName$EventArgs($type$ $argName$) { this.$propertyName$ = $argName$; } public $type$ $propertyName$ { get; private set; } } public event EventHandler<$eventName$EventArgs> $eventName$; protected virtual void On$eventName$($eventName$EventArgs e) { var handler = $eventName$; if (handler != null) handler(this, e); }]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>
这里有两个属性:
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Generic event with two types/arguments.</Title> <Shortcut>ev2Generic</Shortcut> <Description>Code snippet for event handler and On method</Description> <Author>Kyralessa</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>type1</ID> <ToolTip>Type of the first property in the EventArgs subclass.</ToolTip> <Default>propertyType1</Default> </Literal> <Literal> <ID>arg1Name</ID> <ToolTip>Name of the first argument in the EventArgs subclass constructor.</ToolTip> <Default>property1Name</Default> </Literal> <Literal> <ID>property1Name</ID> <ToolTip>Name of the first property in the EventArgs subclass.</ToolTip> <Default>Property1Name</Default> </Literal> <Literal> <ID>type2</ID> <ToolTip>Type of the second property in the EventArgs subclass.</ToolTip> <Default>propertyType1</Default> </Literal> <Literal> <ID>arg2Name</ID> <ToolTip>Name of the second argument in the EventArgs subclass constructor.</ToolTip> <Default>property1Name</Default> </Literal> <Literal> <ID>property2Name</ID> <ToolTip>Name of the second property in the EventArgs subclass.</ToolTip> <Default>Property2Name</Default> </Literal> <Literal> <ID>eventName</ID> <ToolTip>Name of the event</ToolTip> <Default>NameOfEvent</Default> </Literal> </Declarations> <Code Language="CSharp"> <![CDATA[public class $eventName$EventArgs : System.EventArgs { public $eventName$EventArgs($type1$ $arg1Name$, $type2$ $arg2Name$) { this.$property1Name$ = $arg1Name$; this.$property2Name$ = $arg2Name$; } public $type1$ $property1Name$ { get; private set; } public $type2$ $property2Name$ { get; private set; } } public event EventHandler<$eventName$EventArgs> $eventName$; protected virtual void On$eventName$($eventName$EventArgs e) { var handler = $eventName$; if (handler != null) handler(this, e); }]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>
我们可以按照模式来创建具有任意数量的属性的属性。
使用通用事件处理程序实例
在.NET Framework 2.0之前,为了将自定义信息传递给事件处理程序,必须声明一个新的委托,该委托指定了一个派生自System.EventArgs类的类。在.NET中不再如此
Framework 2.0,引入了System.EventHandler <T>)委托。此泛型委托允许从EventArgs派生的任何类与事件处理程序一起使用。