.NET EventHandlers-通用还是不通用?

时间:2020-03-06 14:41:09  来源:igfitidea点击:

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