c# - studio - .NET EventHandlers: genérico o no?
handler c# (9)
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:
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 una nueva subclase de delegado y EventArgs
para cada tipo de evento. ¿Hay algún problema con mi enfoque genérico?
EventHandler<TEventArgs>
genérico EventHandler<TEventArgs>
, por lo que no necesita crear el delegado genérico, pero aún necesita la EventArgs<T>
genérica EventArgs<T>
, porque TEventArgs: EventArgs
. Otro EDIT: Una desventaja (para mí) de la solución incorporada es la verbosidad adicional: public event EventHandler<EventArgs<Foo>> FooChanged;
vs.
public event EventHandler<Foo> FooChanged;
Sin embargo, puede ser difícil para los clientes registrarse para sus eventos, ya que el espacio de nombres del sistema se importa de forma 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. ?
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.
/EDITAR
No obtuve la distinción allí originalmente. 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.
Delegado del siguiente formulario se ha agregado desde .NET Framework 2.0
public delegate void EventHandler<TArgs>(object sender, TArgs args) where TArgs : EventArgs
Su enfoque va un poco más allá, ya que proporciona una implementación lista para usar con EventArgs con un solo elemento de datos, pero carece de varias propiedades de la idea original:
- 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.
- Su objeto de datos es genérico, pero también es "anónimo", y mientras lee el código tendrá que descifrar la propiedad "Artículo" de los usos. Debe nombrarse de acuerdo con los datos que proporciona.
- Al usar genéricos de esta manera, no puede hacer una jerarquía paralela de EventArgs, cuando tiene una jerarquía de tipos subyacentes (elementos). Eg EventArgs <BaseType> no es tipo base para EventArgs <DerivedType>, incluso si BaseType es base para DerivedType.
Por lo tanto, creo que es mejor usar EventHandler <T> genérico, pero todavía tienen clases EventArgs personalizadas, organizadas de acuerdo con los requisitos 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.
Desde .NET 2.0
EventHandler<T>
ha sido implementado.
Esta es la implementación correcta. Se ha agregado al .NET Framework (mscorlib) ya que los primeros genéricos están disponibles (2.0).
Para obtener más información sobre su uso e implementación, consulte MSDN: http://msdn.microsoft.com/en-us/library/db0etb8x.aspx
La primera vez que vi este pequeño patrón, estaba usando el bloque de aplicación de interfaz de usuario compuesta , del grupo de patrones y prácticas de MS.
No me arroja ninguna bandera roja; de hecho, es incluso una forma inteligente de aprovechar los genéricos para seguir la regla DRY .
No, no creo que este sea el enfoque equivocado. Creo que incluso es recomendado en el [fantástico] libro Framework Design Guidelines . Hago lo mismo.
Para facilitar la declaración genérica de eventos, creé un par de fragmentos de código para ello. Para usarlos:
- Copia el fragmento completo.
- Pégalo en un archivo de texto (por ejemplo, en el Bloc de notas).
- Guarde el archivo con una extensión .snippet.
- Coloque el archivo .snippet en su directorio de fragmentos de código apropiado, como por ejemplo:
Visual Studio 2008 / Snippets de código / Visual C # / Mis fragmentos de código
Aquí hay uno que usa una clase personalizada de EventArgs 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í hay 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.
Puede encontrar Generic EventHandler en MSDN http://msdn.microsoft.com/en-us/library/db0etb8x.aspx
He estado utilizando EventHandler genérico extensivamente y pude evitar el llamado "Explosion of Types (Classes)" El proyecto se mantuvo más pequeño y más fácil de navegar.
Venir con un nuevo delegado intuitivo para un delegado de EventHandler no genérico es doloroso y se superpone con los tipos existentes Agregar "* EventHandler" al nuevo nombre de delegado no ayuda mucho en mi opinión
Usar instancias genéricas de controlador de eventos
Antes de .NET Framework 2.0, para pasar información personalizada al controlador de eventos, se tenía que declarar un nuevo delegado que especificaba 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.