Pregunta

Cada vez que empiezo 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 un nuevo delegado y una subclase EventHandler<TEventArgs> para cada tipo de evento. ¿Hay algo mal con mi enfoque genérico?


EDITAR: La razón de esta publicación es que acabo de volver a crear esto en un nuevo proyecto y quería asegurarme de que estuviera bien. En realidad, lo estaba recreando como lo publiqué. Descubrí que hay un EventArgs<T> genérico, por lo que no necesita crear el delegado genérico, pero aún necesita la clase genérica TEventArgs: EventArgs, porque <=>.
Otra EDICIÓN: 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 una molestia para los clientes registrarse para sus eventos, porque el espacio de nombres del Sistema se importa de manera predeterminada, por lo que tienen que buscar manualmente su espacio de nombres, incluso con una herramienta sofisticada como Resharper ... Cualquiera tiene alguna idea perteneciente a eso?

¿Fue útil?

Solución

Se ha agregado un delegado del siguiente formulario 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 inmediata para EventArgs con un solo 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 tendrá que descifrar el " Elemento " propiedad de 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 (elemento). P.ej. EventArgs & Lt; BaseType & Gt; no es tipo base para EventArgs < DerivedType > ;, incluso si BaseType es base para DerivedType.

Entonces, creo que es mejor usar EventHandler genérico < T > ;, pero aún tener clases EventArgs personalizadas, organizadas de acuerdo con los requisitos del modelo de datos. Con Visual Studio y extensiones como ReSharper, solo es cuestión de unos pocos comandos crear una nueva clase como esa.

Otros consejos

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

  • Copie todo el fragmento.
  • 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 apropiado, como:

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

Aquí hay uno que usa una clase EventArgs personalizada 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 una 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.

No, no creo que este sea el enfoque equivocado. Creo que incluso se recomienda en el [fantástico] libro Directrices de diseño del marco . Yo hago lo mismo.

Esta es la implementación correcta. Se ha agregado a .NET Framework (mscorlib) desde que los genéricos estuvieron disponibles por primera vez (2.0).

Para obtener más información sobre su uso e implementación, consulte MSDN: http: // msdn .microsoft.com / es-es / biblioteca / db0etb8x.aspx

La primera vez que vi este pequeño patrón, estaba usando Bloque de aplicación de IU compuesto , de MS Patterns & amp; Grupo de prácticas.

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

Desde .NET 2.0

  

EventHandler<T>

se ha implementado.

Puede encontrar Generic EventHandler en MSDN http://msdn.microsoft. com / es-us / library / db0etb8x.aspx

He estado usando EventHandler genérico ampliamente y pude evitar la llamada " Explosión de tipos (clases) " El proyecto se mantuvo más pequeño y más fácil de navegar.

Crear un nuevo delegado intuitivo para un delegado EventHandler no genérico es doloroso y se superpone con los tipos existentes Anexando & Quot; * EventHandler & Quot; al nuevo nombre del delegado no ayuda mucho en mi opinión

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

/ EDITAR

No obtuve la distinción allí originalmente. Mientras esté devolviendo una clase que hereda de EventArgs, que es usted, no veo ningún problema. Me preocuparía si no estuvieras envolviendo el resultado por razones de mantenimiento. Sigo diciendo que me parece bien.

Usar instancias de controlador de eventos genéricos

Antes de .NET Framework 2.0, para pasar información personalizada al controlador de eventos, se debía declarar un nuevo delegado que especificara 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 que cualquier clase derivada de EventArgs se use con el controlador de eventos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top