#395 – Overriding the Default add and remove Accessors for an Event

When you use the event keyword to define an event, the compiler automatically creates a private backing variable that is a delegate instance, as well as code that encapsulates adding and removing methods to the delegate’s invocation list.

        public event EventHandler Barked;


You can also explicitly declare the backing variable and the add/remove accessors.  The following code results in equivalent behavior to the code shown above.

        private EventHandler barked;
        public event EventHandler Barked
        {
            add { barked += value; }
            remove { barked -= value; }
        }

To the client code, this looks the same–we just have a Barked event. But you could now add some custom code, e.g. logging.

        private EventHandler barked;
        public event EventHandler Barked
        {
            add
            {
                barked += value;
                Logger.Log(string.Format("Added handler {0} ({1})", value.Method.Name, value.Method.ReflectedType));
            }

            remove
            {
                barked -= value;
                Logger.Log(string.Format("Removed handler {0} ({1})", value.Method.Name, value.Method.ReflectedType));
            }
        }

#394 – Raising an Event in a Thread-Safe Manner

When you raise an event, you typically check to make sure that the event is not null, i.e. that at least one handler exists.   This method could result in a null reference exception if another thread removes all handlers after we check for null, but before we raise the event.

A more thread-safe approach is to make a copy of the event before raising it.  If a different thread removes all handlers from the original event, the copy is unaffected.

        public event EventHandler Barked;
        protected virtual void OnBarked()
        {
            EventHandler eventCopy = Barked;

            if (eventCopy != null)
                eventCopy(this, null);
        }

A cleaner approach, also thread-safe, is to declare the event with a null handler.  We can then avoid checking for null, since there will always be at least one handler.

        public event EventHandler Barked = delegate { };

        protected virtual void OnBarked()
        {
            Barked(this, null);
        }

#393 – Implement a Helper Method to Raise an Event

You typically raise an event from within a class by first checking to see if the event is null, i.e. if there are any handlers defined for the event.

        public void Bark(string barkSound)
        {
            Console.WriteLine("{0} says {1}", Name, barkSound);

            if (Barked != null)
                Barked(this, new BarkEventArgs(Name, barkSound));
        }

It’s common practice, however, to define a helper method in your class that actually raises the event.  This method encapsulates the logic of raising the event, including the check for null.  If you also define this method as protected virtual, future subclasses of your class can override the helper method to override the behavior of raising the event.

        public void Bark(string barkSound)
        {
            Console.WriteLine("{0} says {1}", Name, barkSound);
            OnBarked(barkSound);
        }

        protected virtual void OnBarked(string barkSound)
        {
            if (Barked != null)
                Barked(this, new BarkEventArgs(Name, barkSound));
        }

#390 – Using the Same Handler for Multiple Events

You can attach a single event handler (method) to more than one event, originating from more than one object.

In the example below, we have a general DogMadeSound handler that we attach to the Barked and Growled events of one Dog instance (Kirby) and to the Barked event of another Dog (Jack).  When any of these events fire, our handler will get called.

        static void Main()
        {
            Dog kirby = new Dog("Kirby");
            Dog jack = new Dog("Jack");

            // We handle Kirby's barks and growls and Jack's barks
            kirby.Barked += new EventHandler<DogEventArgs>(DogMadeSound);
            kirby.Growled += new EventHandler<DogEventArgs>(DogMadeSound);
            jack.Barked += new EventHandler<DogEventArgs>(DogMadeSound);

            kirby.Bark("Woof");
            kirby.Growl();
            jack.Bark("Yap");
        }

        static void DogMadeSound(object sender, DogEventArgs e)
        {
            Console.WriteLine("DogMadeSound handler: {0} - {1}", e.DogName, e.Sound);
        }

#389 – Check for Null Before Raising an Event

You should always check to see if an event is null, before trying to raise it.

            if (Barked != null)
                Barked(this, new SomebodyBarkedEventArgs(Name, barkSound));

When no handlers are attached to the event, it will have a null value. If you try raising the event when it is null, you’ll get a NullReferenceException.

Alternatively, you could avoid having to check for null if you attach an empty handler to the event when you declare it.

        public event EventHandler<SomebodyBarkedEventArgs> Barked = delegate { };

#388 – Declaring and Using Private Events

You can declare private events in a class and they can be either instance or static events.  A private event is one that code within the class can subscribe to, but that is not visible to code outside the class.

For example, we can create a private static event in the Dog class that will fire whenever a new dog is created.

        private static event EventHandler<DogCreatedEventArgs> DogCreated;

This event will be visible only to code within the Dog class.

In the static constructor, we attach a handler that will record each Dog created.

        static Dog()
        {
            Dog.DogCreated += new EventHandler<DogCreatedEventArgs>(Dog_DogCreated);
        }

        static List<string> DogList = new List<string>();
        static void Dog_DogCreated(object sender, DogCreatedEventArgs e)
        {
            DogList.Add(e.Name);
        }

Finally, we fire the event from the instance constructor.

        public Dog(string name)
        {
            Name = name;

            if (Dog.DogCreated != null)
                Dog.DogCreated(this, new DogCreatedEventArgs(name));
        }

#387 – Static Events vs. Static Event Handler Methods

Events can be static events or instance events.  Static events are associated with the class as a whole, whereas instance events are associated with a specific instance of the class.

You can attach event handlers to both static and instance events.

            // Add handler to static event
            Dog.SomebodyBarked += new EventHandler<SomebodyBarkedEventArgs>(Dog_SomebodyBarked);

            Dog kirby = new Dog("Kirby");

            // Add handler to instance event
            kirby.Barked += new EventHandler<SomebodyBarkedEventArgs>(kirby_Barked);

In both cases, we’re attaching a handler that is a static method.

        static void Dog_SomebodyBarked(object sender, SomebodyBarkedEventArgs e)
        {
            Console.WriteLine("Dog {0} barked, saying {1}", e.DogName, e.Bark);
        }

We can also attach a handler that is an instance method.  So we can attach both static and instance methods as handlers, to either static or instance events.

For example:

            BarkLogger log = new BarkLogger();

            // Add instance method as handler for static event
            Dog.SomebodyBarked += new EventHandler<SomebodyBarkedEventArgs>(log.LogBark);

#386 – Implementing a Static Event

You can implement static events in the same way that you implement normal (instance) events.  Like other static members, static events are associated with the class itself, rather than with an instance of the class.

An implementation of a static event (assumes existence of SomebodyBarkedEventArgs class):

        public static event EventHandler<SomebodyBarkedEventArgs> SomebodyBarked;

        // In instance method, raise the static event
        public void Bark(string barkSound)
        {
            Console.WriteLine("{0} says {1}", Name, barkSound);

            if (Dog.SomebodyBarked != null)
                Dog.SomebodyBarked(null, new SomebodyBarkedEventArgs(Name, barkSound));
        }

Adding an event handler for this event:

        static void Main()
        {
            // Tell me when any dog barks
            Dog.SomebodyBarked += new EventHandler<SomebodyBarkedEventArgs>(Dog_SomebodyBarked);

            Dog kirby = new Dog("Kirby");
            Dog jack = new Dog("Jack");

            // Pesky dogs bark
            jack.Bark("Grrrr");
            kirby.Bark("Woof");
        }

        static void Dog_SomebodyBarked(object sender, SomebodyBarkedEventArgs e)
        {
            Console.WriteLine("Dog {0} barked, saying {1}", e.DogName, e.Bark);
        }

#384 – The Difference Between Delegates and Events

Delegates are not the same thing as events in C#.

Delegates

  • A delegate type defines a signature for a method (number and type of parameters and return type)
  • A delegate instance is an instance of a delegate type that can refer to one or more methods
  • Can add/remove handlers using the +=, -= operators
  • Can invoke handlers by calling delegate instance like a method

Events

  • Is a class member representing something that the class might notify calling code about
  • Declared in the class like a field, whose type is a delegate type
  • Wraps a private member variable that is a delegate instance
  • Wraps private methods that add/remove methods to the delegate instance’s invocation list
  • Client code can add/remove handlers using the +=, -= operators
  • Client code can’t invoke directly

Basically, an event is a mechanism that uses a delegate to notify client code of something without exposing the delegate’s invocation list to the client.

#383 – Removing a Handler from an Event

You can use the += operator to add a handler to an event.  Because an event can invoke multiple handlers, you can use the += operator to add additional handlers to the same event.  When the event is raised, all of the handlers will be called.

            kirby.Barked += kirby_Barked;
            kirby.Barked += kirby_Barked2;

        static void kirby_Barked(object o, EventArgs e)
        {
            Console.WriteLine("I see that Kirby barked");
        }

        static void kirby_Barked2(object o, EventArgs e)
        {
            Console.WriteLine("I ALSO see that Kirby barked");
        }


It’s also possible to remove a particular handler for the list of methods that an event will invoke, using the -= operator.

            kirby.Barked -= kirby_Barked;

Follow

Get every new post delivered to your Inbox.

Join 37 other followers