2008-09-24 9 views
19

Cada vez que comienzo en lo profundo de un proyecto de C#, termino con muchos eventos que realmente solo necesitan pasar un solo elemento. Me quedo con la práctica EventHandler/EventArgs, pero lo que me gusta hacer es tener algo como:.NET EventHandlers - ¿Genérico o no?

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

Más tarde, puedo tener mi

public event EventHandler<Foo> FooChanged; 

public event EventHandler<Bar> BarChanged; 

Sin embargo, parece que el estándar para .NET es crear un nuevo delegado y una subclase EventArgs para cada tipo de evento. ¿Hay algún problema con mi enfoque genérico?


EDITAR: El motivo de esta publicación es que acabo de volver a crear esto en un nuevo proyecto, y quería asegurarme de que estaba bien. En realidad, estaba volviendo a crearlo cuando publiqué. Descubrí que hay un genérico EventHandler<TEventArgs>, por lo que no necesita crear el delegado genérico, pero aún necesita la clase genérica EventArgs<T>, porque TEventArgs: EventArgs.
Otra EDIT: Una desventaja (para mí) de la solución integrada es el nivel de detalle adicional:

public event EventHandler<EventArgs<Foo>> FooChanged; 

vs

public event EventHandler<Foo> FooChanged; 

Puede ser un dolor para los clientes registrarse para su sin embargo, debido a que el espacio de nombres del sistema se importa de manera predeterminada, por lo que deben buscar su espacio de nombre manualmente, incluso con una herramienta de lujo como Resharper ... ¿Alguien tiene alguna idea relacionada con eso?

+1

Para el estilo CRUD que mencionaste en el otro comentario, tendría EntityEventHandler que sería suficientemente autodescriptivo. –

+1

se podría agregar: usando FooArgs = Namespace.EventArgs ; que limpiaría el evento hasta - evento público EventHandler FooChanged; – Fraser

Respuesta

26

Delegado de la siguiente formulario se ha añadido desde .NET Framework 2,0

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

enfoque que va un poco más allá, ya que proporciona fuera de la caja para la aplicación EventArgs con único elemento de datos, pero carece de varias propiedades de la idea original :

  1. No puede agregar más propiedades a los datos del evento sin cambiar el código dependiente. Deberá cambiar la firma del delegado para proporcionar más datos al suscriptor del evento.
  2. Su objeto de datos es genérico, pero también es "anónimo", y mientras lee el código deberá descifrar la propiedad "Artículo" de los usos. Debe nombrarse de acuerdo con los datos que proporciona.
  3. Usando genéricos de esta manera no puede hacer una jerarquía paralela de EventArgs, cuando tiene una jerarquía de tipos subyacentes (elementos). P.ej. EventArgs <BaseType> no es el tipo base para EventArgs <DerivedType>, incluso si BaseType es base para DerivedType.

Por lo tanto, creo que es mejor utilizar manejador de sucesos genérica <T>, pero todavía tienen clases EventArgs personalizados, organizados de acuerdo a los requerimientos del modelo de datos. Con Visual Studio y extensiones como ReSharper, es solo cuestión de unos pocos comandos para crear una nueva clase como esa.

+0

Entonces, si tiene un modelo de datos con 20 clases diferentes, ¿crearía un EventArgs para cada uno de ellos? –

+0

@Chris Marasti-Georg Depende del número de eventos diferentes y sus respectivos datos, no del tamaño del modelo. Si todas sus clases activan eventos simples PropertyChanging/PropertyChanged, tendrá un heredero EventArgs con datos PropertyName. –

+0

Suponiendo un servicio de tipo CRUD, tendría 3 eventos por tipo de elemento, ItemCreated, ItemUpdated, ItemDeleted. Creo que los dos primeros deberían pasar una copia del artículo, y el último, quizás su identificación. Si el servicio CRUD puede manejar 20 tipos de elementos, está viendo 21 subclases EventArgs. –

1

Creo que las versiones recientes de .NET tienen definido un gestor de eventos así. Eso es un gran aprobado en lo que a mí respecta.

/EDIT

no recibieron la distinción que hay un principio. Siempre que repases una clase heredada de EventArgs, que eres, no veo ningún problema. Me preocuparía si no estuvieras envolviendo el resultado por razones de mantenimiento. Sigo diciendo que me parece bien.

+0

Las versiones recientes tienen un delegado genérico, pero no tienen un solo elemento EventArgs –

2

Desde .NET 2,0

EventHandler<T>

se ha implementado.

3

La primera vez que vi este pequeño patrón, estaba usando Composite UI Application block, de MS Patterns & Grupo de prácticas.

No me lanza ninguna bandera roja; de hecho, es incluso una forma inteligente de aprovechar los genéricos para seguir la regla DRY.

2

Puede encontrar manejador de sucesos genérica en MSDN http://msdn.microsoft.com/en-us/library/db0etb8x.aspx

He estado usando manejador de sucesos genérica extensamente y fue capaz de evitar los llamados "explosión de los tipos (clases)" Proyecto se mantuvo más pequeño y más fácil de navegar alrededor.

El subir con un nuevo delegado intuitiva para delegado manejador de sucesos no genérico es dolorosa y se superponen con los tipos existentes Anexión "* manejador de sucesos" al nuevo nombre de delegado no ayuda mucho en mi opinión

9

Para facilitar la declaración genérica de eventos, creé un par de fragmentos de código para ella. Para usarlos:

  • Copie todo el fragmento.
  • Pégalo en un archivo de texto (por ejemplo, en el Bloc de notas).
  • Guarde el archivo con la extensión .snippet.
  • poner el archivo .snippet en su directorio fragmento apropiado, como por ejemplo:

Visual Studio 2008 \ fragmentos de código \ Visual C# \ Mis fragmentos de código

Aquí hay uno que utiliza una clase EventArgs personalizados con una propiedad:

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

Y aquí es uno que tiene dos propiedades:

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

Puede seguir el patrón para crearlos con tantas propiedades como desee.

1

utilizar instancias de controlador de eventos genéricos

Antes de .NET Framework 2.0, con el fin de transmitir la información personalizada al controlador de eventos, un nuevo delegado tuvo que ser declarado que especifica una clase derivada de la clase System.EventArgs. Esto ya no es cierto en .NET

Framework 2.0, que introdujo el delegado System.EventHandler < T>). Este delegado genérico permite utilizar cualquier clase derivada de EventArgs con el controlador de eventos.

Cuestiones relacionadas