#1,164 – A Sealed Event Cannot Be Overridden

As with a method, you can use the sealed keyword when overriding a virtual event to indicate that child classes cannot override the event being defined.

When you override an event, you create a virtual event that behaves polymorphically.  The base class defines the event using the virtual keyword and you define the event in your child class using the override keyword.

When you use the override keyword, your event can itself be overridden in a child class.

If you want to override an event, but prevent any derived classes from overriding your implementation, you can use the sealed keyword to indicate that a child class must use the new keyword, rather than overriding your event.

    public class Dog
    {
        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        public virtual event EventHandler<string> Barked;
    }

    public class Terrier : Dog
    {
        public Terrier(string name) : base(name) { }

        private EventHandler<string> terrierBarked;
        public sealed override event EventHandler<string> Barked
        {
            add
            {
                terrierBarked += value;
                // Special Terrier.Barked event logic here
            }
            remove
            {
                terrierBarked -= value;
            }
        }
    }

    public class JackRussell : Terrier
    {
        public JackRussell(string name) : base(name) { }

        public new event EventHandler<string> Barked;
    }
Advertisements

#1,163 – An Abstract Event Has No Implementation

An abstract event is a special kind of virtual event, defined in a base class, which has no implementation of its own.  A derived class must define every abstract event (as well as every other abstract member) using the override keyword.

    public abstract class Dog 
    {
        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        public abstract event EventHandler<string> Barked;

        public virtual void Bark()
        {
            Console.WriteLine("Woof");
        }
    }

    public class Terrier : Dog
    {
        public Terrier(string name) : base(name) { }

        public override event EventHandler<string> Barked;

        public override void Bark()
        {
            Console.WriteLine("Yip yip");
            Barked(this, "Yip yip");
        }
    }

The class in which the abstract event is defined must be defined as an abstract class, using the abstract keyword.

A derived class may not hide the abstract event in the base class, using the new keyword.  It must implement the inherited abstract event and the new event must be virtual, defined using the override keyword.

Example of using this event:

            Terrier t = new Terrier("Jack");
            t.Barked += (o, s) => Console.WriteLine("- " + s + " -");
            t.Bark();

1163-001

#1,162 – Virtual Events Support Polymorphism

In C#, polymorphism is implemented using virtual members–which can be methods, properties, indexers or events.

A virtual event has an implementation in the base class that can be overridden in a derived class.  When the event’s add or remove accessors are invoked, the specific accessor used is determined at run-time based on the type of the underlying object.

A virtual event is defined in the base class using the virtual keyword.

        // Code from Dog class
        private EventHandler barked;
        public virtual event EventHandler Barked
        {
            add
            {
                barked += value;
                Console.WriteLine("Dog.Barked add accessor");
            }
            remove
            {
                barked -= value;
                Console.WriteLine("Dog.Barked remove accessor");
            }
        }

A virtual event is overridden in a derived class using the override keyword.

        // Code from Terrier : Dog class
        private EventHandler myBarked;
        public override event EventHandler Barked
        {
            add
            {
                myBarked += value;
                Console.WriteLine("Terrier.Barked add accessor");
            }
            remove
            {
                myBarked -= value;
                Console.WriteLine("Terrier.Barked remove accessor");
            }
        }

Using the event:

            Dog d = new Dog("Bob");
            d.Barked += (s, e) => { Console.WriteLine("Dog.Barked"); };

            Terrier t = new Terrier("Jack");
            t.Barked += (s, e) => { Console.WriteLine("Terrier.Barked"); };

            Dog d2 = t;
            d2.Barked += (s, e) => { Console.WriteLine("Terrier.Barked"); };

1162-001

#1,161 – Explicit Implementation of Events in Interface

If you implement an event defined in an interface explicitly, you must use event accessor syntax, defining both add and remove accessors.

    public interface IDogEvents
    {
        event EventHandler Barked;
        event EventHandler Escaped;
    }

    public class Dog : IDogEvents
    {
        public string Name { get; set; }
        public bool IsPresent { get; set; }

        public Dog(string name)
        {
            Name = name;
            IsPresent = true;
        }

        public void Bark()
        {
            Console.WriteLine("Woof");
            if (IsPresent)
                barked(this, EventArgs.Empty);
            else
                escaped(this, EventArgs.Empty);
        }

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

        private EventHandler escaped;
        event EventHandler IDogEvents.Escaped
        {
            add { escaped += value; }
            remove { escaped -= value; }
        }
    }

You can then add an event handler by using an interface reference.

        static void Main(string[] args)
        {
            Dog d = new Dog("Bob");
            IDogEvents ide = (IDogEvents)d;
            ide.Barked += d_Barked;
            d.Bark();
        }

#1,160 – Implementing Events Defined in an Interface

A class that implements an interface needs to define any events that are part of the interface.  Below is an example.

    public interface IDogEvents
    {
        event EventHandler Barked;
        event EventHandler Escaped;
    }

    public class Dog : IDogEvents
    {
        public string Name { get; set; }
        public bool IsPresent { get; set; }

        public Dog(string name)
        {
            Name = name;
            IsPresent = true;
        }

        public void Bark()
        {
            Console.WriteLine("Woof");
            if (IsPresent)
                Barked(this, EventArgs.Empty);
            else
                Escaped(this, EventArgs.Empty);
        }

        public event EventHandler Barked = delegate { };

        public event EventHandler Escaped = delegate { };
    }

#1,159 – Events vs. Delegates, part II

You can use a delegate in place of an event by simply removing the event keyword.  Using an event is preferred, however, because the event syntax prevents different consumers of the event from impacting each other.

Specifically, using a public field that is a delegate allows client code to do any of the following.  These actions are all prohibited when using an event.

  • Prevent other subscribers from being invoked by directly changing the delegate’s invocation list.
  • Delete invocation list by reassigning the delegate instance to null
  • Invoke the delegate directly

These are all actions that should only be allowed by the producer of the event and not by any of its consumers.  The event syntax prevents consumers from interfering with each other.

#1,158 – Events vs. Delegates, part I

You’ll typically define an event rather than using a public delegate.  Although either method will work, events provide some benefits over using delegates directly.

Below, we define a delegate type and a Barked event of that type in a Dog class:

    public delegate void DogDelegate(string dogName);

    public class Dog
    {
        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        public event DogDelegate Barked = delegate { };

        public void Bark()
        {
            Console.WriteLine("Woof");
            Barked(Name);
        }
    }

We can now handle the event as follows:

        static void Main(string[] args)
        {
            Dog d = new Dog("Bob");
            d.Barked += d_Barked;
            d.Bark();

            Console.ReadLine();
        }

        static void d_Barked(string dogName)
        {
            Console.WriteLine(dogName + " just barked");
        }

1158-001
This code would all work the same if we simply removed the event keyword, making the delegate available to the client code.  (See part II for more information).