Pergunta

Toda vez que eu começar em profundidade em um projeto C #, eu acabar com lotes de eventos que realmente só precisa passar um único item. Eu ficar com a prática EventHandler / EventArgs, mas o que eu gostaria de fazer é ter algo como:

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

Mais tarde, eu posso ter meu

public event EventHandler<Foo> FooChanged;

public event EventHandler<Bar> BarChanged;

No entanto, parece que o padrão for .NET é criar um novo delegado e EventArgs subclasse para cada tipo de evento. Há algo de errado com a minha abordagem genérica?


EDIT: A razão para este post é que eu apenas re-criou esta em um novo projeto, e queria ter certeza de que era ok. Na verdade, eu estava recriando-lo como eu postei. Descobri que há uma EventHandler<TEventArgs> genérico, para que você não precisa criar o delegado genérico, mas você ainda precisa da classe EventArgs<T> genérico, porque TEventArgs: EventArgs.
Outro EDIT: Uma desvantagem (para mim) do built-in solução é a verbosidade extra:

public event EventHandler<EventArgs<Foo>> FooChanged;

vs.

public event EventHandler<Foo> FooChanged;

Pode ser uma dor para os clientes para se inscrever para os seus eventos, porém, porque o namespace System é importado por padrão, então eles têm que procurar manualmente o seu namespace, mesmo com uma ferramenta extravagante como ReSharper ... Alguém tem alguma idéia pertencente a isso?

Foi útil?

Solução

Representante da seguinte forma foi adicionado desde Framework 2.0

public delegate void EventHandler<TArgs>(object sender, TArgs args) where TArgs : EventArgs

Você se aproxima vai um pouco mais longe, uma vez que você fornecer out-of-the-box implementação para EventArgs com item de dados único, mas falta-lhe várias propriedades da idéia original:

  1. Você não pode adicionar mais propriedades para os dados do evento sem alterar o código dependente. Você vai ter que mudar a assinatura do delegado para fornecer mais dados para o assinante do evento.
  2. O seu objeto de dados é genérico, mas também é "anônimo", e ao ler o código que você terá que decifrar a propriedade "Item" de usos. Ele deve ser nomeado de acordo com os dados que ele fornece.
  3. Usando genéricos desta forma você não pode fazer hierarquia paralela de EventArgs, quando você tem hierarquia de subjacentes tipos (item). Por exemplo. EventArgs não é o tipo de base para EventArgs , mesmo se BaseType é base para derivedtype.

Então, eu acho que é melhor usar EventHandler genérica , mas ainda têm aulas EventArgs personalizados, organizados de acordo com os requisitos do modelo de dados. Com o Visual Studio e extensões como ReSharper, é apenas uma questão de alguns comandos para criar nova classe assim.

Outras dicas

Para fazer declaração de evento genérico mais fácil, eu criei um par de trechos de código para ele. Para usá-los:

  • Copiar todo o trecho.
  • Cole-o em um arquivo de texto (por exemplo, no Bloco de Notas).
  • Salve o arquivo com uma extensão .snippet.
  • Coloque o arquivo .snippet em seu diretório trecho apropriado, tais como:

Visual Studio 2008 \ trechos de código \ Visual C # \ My trechos de código

Aqui está um que usa uma classe EventArgs personalizados com uma propriedade:

<?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>

E aqui está um que tem duas propriedades:

<?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>

Você pode seguir o padrão para criá-los com tantas propriedades como você gosta.

Não, eu não acho que esta é a abordagem errada. Eu acho que é mesmo recomendado na [fantástico] livro Framework Design Guidelines . Eu faço a mesma coisa.

Esta é a implementação correta. Foi adicionado ao .NET Framework (mscorlib) desde genéricos veio pela primeira vez disponíveis (2,0).

Para saber mais sobre o seu uso e implementação ver MSDN: http: // MSDN .microsoft.com / en-us / library / db0etb8x.aspx

A primeira vez que vi este padrão pouco, eu estava usando composto Aplicação IU bloco , a partir de MS Patterns & grupo Práticas.

Ele não lança qualquer bandeira vermelha para mim; na verdade, é ainda uma maneira inteligente de alavancar genéricos para seguir o regra DRY.

Desde .NET 2.0

EventHandler<T>

foi implementado.

Você pode encontrar EventHandler genérico no MSDN http://msdn.microsoft. com / en-us / library / db0etb8x.aspx

Eu estive usando EventHandler genérico extensivamente e foi capaz de evitar os chamados "explosão de tipos (classes)" Projeto foi mantido menor e mais fácil de navegar.

Chegando-se com um novo delegado um intuitivo para delegado EventHandler não-genérico é doloroso e sobreposição com tipos existentes Acrescentando "* EventHandler" para novo nome delegado não ajuda muito na minha opinião

Eu acredito que as versões recentes do .NET tem apenas como um manipulador de eventos definidos neles. Isso é um grande polegares para cima, tanto quanto eu estou em causa.

/ EDIT

Não recebeu a distinção lá originalmente. Contanto que você está passando para trás uma classe que herda de EventArgs, que você é, eu não vejo um problema. Eu estaria preocupado se não estivesse embrulhando as razões de manutenção resultfor. Ainda digo que parece ser bom para mim.

Utilize casos manipulador de evento genérico

Antes de .NET Framework 2.0, a fim de passar informações personalizadas para o manipulador de eventos, um novo delegado teve de ser declarado que o especificado uma classe derivada da classe System.EventArgs. Isso não é mais verdade no .NET

é

Framework 2.0, que introduziu o delegado System.EventHandler ). Este delegado genérico permite que qualquer classe derivada de EventArgs para ser usado com o manipulador de eventos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top