Question

Chaque fois que je commence en profondeur dans un projet C #, je me retrouve avec de nombreux événements qui ne nécessitent que le passage d’un élément à un autre. Je m'en tiens à la pratique EventHandler / EventArgs, mais ce que j'aime faire, c'est avoir quelque chose comme:

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

Plus tard, je peux avoir mon

public event EventHandler<Foo> FooChanged;

public event EventHandler<Bar> BarChanged;

Cependant, il semble que la norme pour .NET consiste à créer un nouveau délégué et une EventHandler<TEventArgs> sous-classe pour chaque type d'événement. Y a-t-il un problème avec mon approche générique?


EDIT: La raison de cet article est que je viens de le recréer dans un nouveau projet et que je voulais être sûr que tout allait bien. En fait, je le recréais comme je l'ai posté. J'ai constaté qu'il existe un générique EventArgs<T>, vous n'avez donc pas besoin de créer le délégué générique, mais vous avez toujours besoin de la classe générique TEventArgs: EventArgs, car <=>. Un autre EDIT: Un inconvénient (pour moi) de la solution intégrée est la verbosité supplémentaire:

public event EventHandler<EventArgs<Foo>> FooChanged;

vs.

public event EventHandler<Foo> FooChanged;

Cela peut toutefois être difficile pour les clients de s’inscrire à vos événements, car l’espace de noms System est importé par défaut. Ils doivent donc rechercher manuellement votre espace de noms, même avec un outil sophistiqué tel que Resharper ... Tout le monde a des idées se rapportant à cela?

Était-ce utile?

La solution

Le délégué du formulaire suivant a été ajouté depuis .NET Framework 2.0

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

Votre approche va un peu plus loin, puisque vous fournissez une implémentation prête à l'emploi pour EventArgs avec un seul élément de données, mais il manque plusieurs propriétés de l'idée initiale:

  1. Vous ne pouvez pas ajouter plus de propriétés aux données d'événement sans changer le code dépendant. Vous devrez modifier la signature du délégué pour fournir plus de données à l'abonné de l'événement.
  2. Votre objet de données est générique, mais il est également & "anonyme &"; et en lisant le code, vous devrez déchiffrer le & "Item &"; propriété des usages. Il doit être nommé en fonction des données fournies.
  3. En utilisant les génériques de cette manière, vous ne pouvez pas créer de hiérarchie parallèle d'EventArgs, lorsque vous avez une hiérarchie de types (d'élément) sous-jacents. Par exemple. EventArgs & Lt; BaseType & Gt; n'est pas un type de base pour EventArgs < DerivedType > ;, même si BaseType est à la base de DerivedType.

Donc, je pense qu'il est préférable d'utiliser le gestionnaire d'événements générique EventHandler < T > ;, mais que vous disposiez toujours de classes EventArgs personnalisées, organisées en fonction des exigences du modèle de données. Avec Visual Studio et des extensions telles que ReSharper, il suffit de quelques commandes pour créer une nouvelle classe de ce type.

Autres conseils

Pour faciliter la déclaration générique d'événements, j'ai créé quelques extraits de code. Pour les utiliser:

  • Copiez l'extrait entier.
  • Collez-le dans un fichier texte (par exemple, dans le Bloc-notes).
  • Enregistrez le fichier avec l'extension .snippet.
  • Placez le fichier .snippet dans le répertoire d'extraits approprié, tel que:

Visual Studio 2008 \ Extraits de code \ Visual C # \ Mes extraits de code

En voici une qui utilise une classe EventArgs personnalisée avec une propriété:

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

Et en voici un qui a deux propriétés:

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

Vous pouvez suivre le modèle pour les créer avec autant de propriétés que vous le souhaitez.

Non, je ne pense pas que ce soit une mauvaise approche. Je pense que cela est même recommandé dans le [fantastique] livre Guide de conception de framework . Je fais la même chose.

Ceci est la bonne implémentation. Il a été ajouté au .NET Framework (mscorlib) depuis la première disponibilité des génériques (2.0).

Pour plus d'informations sur son utilisation et son implémentation, voir MSDN: http: // msdn .microsoft.com / fr-us / library / db0etb8x.aspx

La première fois que j'ai vu ce petit motif, j’utilisais Bloc d'application d'interface utilisateur composite , à partir de MS Patterns & amp; Groupe de pratique.

Il ne jette aucun drapeau rouge à moi; En fait, il est même judicieux de tirer parti des médicaments génériques en respectant la règle DRY .

Depuis .NET 2.0

  

EventHandler<T>

a été mis en œuvre.

Vous pouvez trouver le gestionnaire d'événements générique sur MSDN http://msdn.microsoft. com / fr-us / library / db0etb8x.aspx

J'utilisais abondamment EventHandler générique et je pouvais empêcher ce qu'on appelle & "Explosion de types (classes) &"; Le projet a été maintenu plus petit et plus facile à naviguer.

Venir avec une nouvelle intuitive, un délégué pour un délégué non générique EventHandler est pénible et se chevauche avec les types existants Ajout de & Quot; * EventHandler & Quot; à mon nouveau nom de délégué ne m'aide pas beaucoup à mon avis

Je pense que les versions récentes de .NET ont précisément défini un tel gestionnaire d'événements. C'est un grand bravo pour moi.

/ EDIT

N'a pas eu la distinction à l'origine. Tant que vous transmettez une classe qui hérite de EventArgs, ce que vous êtes, je ne vois pas de problème. Je serais inquiet si vous ne présentiez pas le résultat pour des raisons de maintenabilité. Je dis toujours que ça me va.

Utiliser des instances de gestionnaire d'événements génériques

Avant .NET Framework 2.0, pour pouvoir transmettre des informations personnalisées au gestionnaire d'événements, il fallait déclarer un nouveau délégué spécifiant une classe dérivée de la classe System.EventArgs. Ce n'est plus vrai dans .NET

Framework 2.0, qui a introduit le délégué System.EventHandler < T >). Ce délégué générique permet à toute classe dérivée de EventArgs d'être utilisée avec le gestionnaire d'événements.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top