Événements – convention de nommage et style

J’apprends des événements / delegates en C #. Puis-je vous demander votre avis sur le style de nommage / de codage que j’ai choisi (tiré du livre Head First C #)?

J’enseigne à un ami à ce sujet demain et j’essaie de trouver la manière la plus élégante d’expliquer les concepts. (pensé que la meilleure façon de comprendre un sujet est d’essayer de l’enseigner!)

class Program { static void Main() { // setup the metronome and make sure the EventHandler delegate is ready Metronome metronome = new Metronome(); // wires up the metronome_Tick method to the EventHandler delegate Listener listener = new Listener(metronome); metronome.OnTick(); } } 

 public class Metronome { // a delegate // so every time Tick is called, the runtime calls another method // in this case Listener.metronome_Tick public event EventHandler Tick; public void OnTick() { while (true) { Thread.Sleep(2000); // because using EventHandler delegate, need to include the sending object and eventargs // although we are not using them Tick(this, EventArgs.Empty); } } } 

 public class Listener { public Listener(Metronome metronome) { metronome.Tick += new EventHandler(metronome_Tick); } private void metronome_Tick(object sender, EventArgs e) { Console.WriteLine("Heard it"); } } 

nb Le code est refait depuis http://www.codeproject.com/KB/cs/simplesteventexample.aspx

Il y a quelques points que je mentionnerais:

Metronome.OnTick ne semble pas être nommé correctement. Sur le plan sémantique, “OnTick” me dit qu’il sera appelé quand il sera “Tick”, mais ce n’est pas vraiment ce qui se passe. Je l’appellerais plutôt “Go”.

Le modèle généralement accepté serait toutefois de faire ce qui suit. OnTick est une méthode virtuelle qui déclenche l’événement. De cette façon, vous pouvez facilement remplacer le comportement par défaut dans les classes héritées et appeler la base pour déclencher l’événement.

 class Metronome { public event EventHandler Tick; protected virtual void OnTick(EventArgs e) { //Raise the Tick event (see below for an explanation of this) var tickEvent = Tick; if(tickEvent != null) tickEvent(this, e); } public void Go() { while(true) { Thread.Sleep(2000); OnTick(EventArgs.Empty); //Raises the Tick event } } } 

Aussi, je sais que c’est un exemple simple, mais s’il n’y a pas d’auditeurs attachés, votre code lancera Tick(this, EventArgs.Empty) . Vous devez au moins inclure un garde null pour rechercher les écouteurs:

 if(Tick != null) Tick(this, EventArgs.Empty); 

Cependant, cela rest vulnérable dans un environnement multithread si l’écouteur n’est pas enregistré entre le garde et l’appel. Le mieux serait de capturer d’abord les auditeurs actuels et de les appeler:

 var tickEvent = Tick; if(tickEvent != null) tickEvent(this, EventArgs.Empty); 

Je sais que c’est une vieille réponse, mais comme elle recueille toujours des votes positifs, voici la façon de faire les choses en C # 6. Tout le concept de “garde” peut être remplacé par un appel de méthode conditionnel et le compilateur fait bien le Right Thing (TM) pour capturer les écouteurs:

 Tick?.Invoke(this, EventArgs.Empty); 

Microsoft a en fait écrit un ensemble complet de directives de nommage et l’a placé dans la bibliothèque MSDN. Vous pouvez trouver les articles ici: Directives pour les noms

En dehors des lignes direcsortingces générales sur la capitalisation, voici ce qu’il a pour les «événements» sur la page Noms de membres de type :

Nommez les événements avec un verbe ou une phrase verbale.

Donne aux noms d’événements un concept d’avant et d’après, en utilisant le présent et le passé. Par exemple, un événement de fermeture qui est déclenché avant la fermeture d’une fenêtre s’appellera Fermeture et un événement déclenché après la fermeture de la fenêtre sera appelé Fermé.

N’utilisez pas les préfixes ou suffixes Avant ou Après pour indiquer les événements pré et post.

Nommez les gestionnaires d’événements (delegates utilisés en tant que types d’événements) avec le suffixe EventHandler.

Utilisez deux parameters nommés expéditeur et e dans les signatures du gestionnaire d’événement.

Le paramètre sender doit être de type Object et le paramètre e doit être une instance ou hériter de EventArgs.

Nommez les classes d’argument d’événement avec le suffixe EventArgs.

Je dirais que le meilleur guide des événements en général, y compris les conventions de nommage, est ici .

C’est la convention que j’ai adoptée, brièvement:

  • Les noms des événements se terminent généralement par un verbe se terminant par -ing ou -ed (Closing / Closed, Loading / Loaded)
  • La classe qui déclare que l’événement doit avoir un On virtuel protégé [EventName] qui doit être utilisé par le rest de la classe pour déclencher l’événement. Cette méthode peut également être utilisée par les sous-classes pour déclencher l’événement et également surchargée pour modifier la logique de remontée des événements.
  • Il y a souvent confusion sur l’utilisation de ‘Handler’ – pour plus de cohérence, tous les delegates doivent être postfixés avec Handler, évitez d’appeler les méthodes qui implémentent les ‘handlers’ Handler
  • La convention de dénomination VS par défaut pour la méthode qui implémente le gestionnaire est EventPublisherName_EventName.

Intéressant comment Microsoft semble rompre ses propres conventions de dénomination avec les noms de gestionnaires d’événements générés par Visual Studio.

Voir: Directives de nommage d’événement (.NET Framework 1.1)

Un point que j’ai trouvé après avoir utilisé des événements dans .Net pendant de nombreuses années est le besoin répétitif de vérifier l’événement pour un gestionnaire null sur chaque invocation. Je n’ai pas encore vu de code en direct qui ne fait rien mais n’appelle pas l’événement s’il est nul.

Ce que j’ai commencé à faire est de mettre un gestionnaire factice à chaque événement que je crée pour enregistrer la nécessité de faire la vérification NULL.

 public class Metronome { public event EventHandler Tick =+ (s,e) => {}; protected virtual void OnTick(EventArgs e) { Tick(this, e); // now it's safe to call without the null check. } } 

OnTick semble bien, mis à part le fait que OnTick ne suit pas le modèle d’invocation d’événement typique. En général, On[EventName] déclenche l’événement une seule fois, comme

 protected virtual void OnTick(EventArgs e) { if(Tick != null) Tick(this, e); } 

Envisagez de créer cette méthode et de renommer votre méthode ” StartTickStartTickStartTick “, et au lieu d’appeler directement Tick depuis StartTick , appelez OnTick(EventArgs.Empty) partir de la méthode StartTick .

Dans votre cas, cela pourrait être:

 class Metronome { event Action Ticked; internalMethod() { // bla bla Ticked(); } } 

Au-dessus de sampple utiliser ci-dessous convention, auto-descriptif;]

Source d’événements:

 class Door { // case1: property change, pattern: xxxChanged public event Action LockStateChanged; // case2: pure action, pattern: "past verb" public event Action Opened; internalMethodGeneratingEvents() { // bla bla ... Opened(true); LockStateChanged(false); } } 

BTW event mot clé est facultatif mais permet de distinguer les «événements» des «rappels»

Écouteur d’événements:

 class AlarmManager { // pattern: NotifyXxx public NotifyLockStateChanged(bool state) { // ... } // pattern: [as above] public NotifyOpened(bool opened) { // OR public NotifyDoorOpened(bool opened) { // ... } } 

Et contraignant [le code a l’air humain]

 door.LockStateChanged += alarmManager.NotifyLockStateChanged; door.Moved += alarmManager.NotifyDoorOpened; 

Même l’envoi manuel d’événements est “lisible par l’homme”.

 alarmManager.NotifyDoorOpened(true); 

Parfois plus expressif peut être “verbe + ing”

 dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting; 

Quelle que soit la convention que vous choisissez, soyez cohérent avec elle.