.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派生的任何类与事件处理程序一起使用。

