Events - naming convention and style

后端 未结 7 648
北海茫月
北海茫月 2020-12-02 10:40

I\'m learning about Events / Delegates in C#. Could I ask your opinion on the naming/coding style I\'ve chosen (taken from the Head First C# book)?

Am teaching a fr

相关标签:
7条回答
  • 2020-12-02 10:57

    I would say the best guide to events in general, including naming conventions, is here.

    It is the convention I have adopted, briefly:

    • Events names are typically terminated with a verb ending with -ing or -ed (Closing/Closed, Loading/Loaded)
    • The class which declares the event should have a protected virtual On[EventName] which should be used by the rest of the class for raising the event. This method can be also used by subclasses to raise the event, and also overloaded to modify the event-raising logic.
    • There is often confusion about the use of 'Handler' - for coherence, all delegates should be postfixed with Handler, try to avoid calling the methods which implement the Handler 'handlers'
    • The default VS naming convention for the method which implements the handler is EventPublisherName_EventName.
    0 讨论(0)
  • 2020-12-02 10:57

    In your case it could be:

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

    Above sampple use below convention, self-describing ;]

    Events source:

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

    BTW. keyword event is optional but enables distinguishing 'events' from 'callbacks'

    Events listener:

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

    And binding [code looks human friendly]

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

    Even manually sending events is "human readable".

    alarmManager.NotifyDoorOpened(true);
    

    Sometimes more expressive can be "verb + ing"

    dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting;
    

    Whichever convention you choose, be consistent with it.

    0 讨论(0)
  • 2020-12-02 11:00

    A point I have found after using events in .Net for many years is the repetitive need to check the event for a null handler on every invocation. I'm yet to see a piece of live code that does anything but not call the event if it is null.

    What I have started doing is to put a dummy handler on every event I create to save the need to do the null check.

    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.
        }
    }
    
    0 讨论(0)
  • 2020-12-02 11:05

    There are a few points that I would mention:

    Metronome.OnTick doesn't seem to be named correctly. Semantically, "OnTick" tells me it will be called when it "Tick"s, but that isn't really what's happening. I would call it "Go" instead.

    The typically accepted model, however would be to do the following. OnTick is a virtual method that raises the event. This way, you can override the default behavior in inherited classes easily, and call the base to raise the event.

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

    Also, I know this is a simple example, but if there are no listeners attached, your code will throw on Tick(this, EventArgs.Empty). You should at least include a null guard to check for listeners:

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

    However, this is still vulnerable in a multithreaded environment if the listener is unregistered between the guard and the invocation. The best would be to capture the current listeners first and call them:

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

    I know this is an old answer, but since it's still gathering upvotes, here's the C# 6 way of doing things. The whole "guard" concept can be replaced with a conditional method call and the compiler does indeed do the Right Thing(TM) in regards to capturing the listeners:

    Tick?.Invoke(this, EventArgs.Empty);
    
    0 讨论(0)
  • 2020-12-02 11:05

    Looks good, aside from the fact that OnTick doesn't follow the typical event invocation model. Typically, On[EventName] raises the event a single time, like

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

    Consider creating this method, and renaming your existing "OnTick" method to "StartTick", and instead of invoking Tick directly from StartTick, call OnTick(EventArgs.Empty) from the StartTick method.

    0 讨论(0)
  • 2020-12-02 11:09

    Interesting how Microsoft seems to break its own naming conventions with Visual Studio generated event handler names.

    See: Event Naming Guidelines (.NET Framework 1.1)

    0 讨论(0)
提交回复
热议问题