#378 – Implementing an Event

You can subscribe to an event in a class by writing an event handler.  You can also implement events in your own class.

An event is just a class member that is an instance of a delegate type and is declared using the event keyword.

Suppose you’ve defined a delegate type that takes a string parameter:

    public delegate void StringHandlerDelegate(string s);

You can now use this delegate type to define an event in a class.  The event is just an instance of the delegate type.

        public event StringHandlerDelegate Barked;

You can now add code in your class to raise the event. We fire the Barked event from the Bark method.

        public void Bark(string barkSound)
        {
            Console.WriteLine(barkSound);

            // Raise the event
            if (Barked != null)
                Barked(barkSound);
        }

Before invoking the delegate, we make sure it’s not null, to avoid an exception at run-time.

Advertisement

#377 – Ensure that All of a Delegate’s Methods Are Called by Using GetInvocationList

When an exception occurs during the invocation of one of the methods a delegate refers to, the delegate does not continue invoking methods in its invocation list.

To ensure that all methods in the delegate’s invocation list are called, even when exceptions occur, you can call GetInvocationList and invoke the methods directly, rather than letting the delegate invoke them.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            // Add two methods to delegate instance
            StringHandlerDelegate del = Method1;
            del += Method2;

            // Invoke
            foreach (StringHandlerDelegate nextDel in del.GetInvocationList())
            {
                try
                {
                    nextDel.Invoke("Yooper");
                }
                catch (Exception xx)
                {
                    Console.WriteLine(string.Format("Exception in {0}: {1}", nextDel.Method.Name, xx.Message));
                }
            }
        }

        static void Method1(string text)
        {
            Console.WriteLine("Method1, {0}", text);
            throw new Exception("Problem in Method1");
        }

        static void Method2(string name)
        {
            Console.WriteLine("Method2, {0}", name);
        }

When you execute the code, you’ll see that Method2 is called, even though Method1 throws an exception.

#376 – What Happens When an Exception Occurs During Delegate Invocation

When you invoke a delegate, it will call each of the methods in its invocation list, one at a time.  If an exception is thrown from within one of those methods, the delegate will stop invoking methods in its invocation list and throw an exception up to the caller that initiated the invocation.  Methods on the invocation list that haven’t already been called will not get called.

In the example below, the delegate’s invocation list has two methods–Method1 and Method2Method1 raises an exception and Method2 is never called.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            try
            {
                // Add two methods to delegate instance
                StringHandlerDelegate del = Method1;
                del += Method2;

                // Invoke
                del("Yooper");
            }
            catch (Exception xx)
            {
                Console.WriteLine("Exception: {0}", xx.Message);
            }
        }

        static void Method1(string text)
        {
            Console.WriteLine("Method1, {0}", text);
            throw new Exception("Problem in Method1");
        }

        static void Method2(string name)
        {
            Console.WriteLine("Method2, {0}", name);
        }

#375 – Using GetInvocationList to Get a List of Individual Delegates

Delegates defined in C# using the delegate keyword are multicast delegates, meaning that they can hold zero or more references to methods.

In the example below, we define a delegate type, create a delegate instance and then point it to two methods.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            // Add two methods to delegate instance
            StringHandlerDelegate del = Method1;
            del += Method2;
        }

At this point, if we invoked the delegate, both methods would be called.

You can use the GetInvocationList method of System.MulticastDelegate to get a list of individual delegates, each of which represents a single method.

            Delegate[] dels = del.GetInvocationList();

Notice that each element in the array, while only representing a single method to be called, is itself an instance of the original multicast delegate, i.e. StringHandlerDelegate.

#374 – A Custom Delegate Derives from System.MulticastDelegate

You define a new delegate in C# using the delegate keyword.

        private delegate void StringHandlerDelegate(string s);

This is equivalent to defining a new class that inherits from System.MulticastDelegate (which in turn inherits from System.Delegate).  MulticastDelegate is a class that knows how to refer to a set of methods–known as as the delegates invocation list.

Later in your code, you declare an instance of your delegate and set it equal to a method.

            StringHandlerDelegate del = Method1;

When you do this, you’re creating an instance of the new MulticastDelegate subclass and giving it a reference to the Method1 method.

You can also add a second method to the invocation list using the += operator.

            del += Method2;

At this point, a new delegate is created and System.Delegate.Combine method is then called to combine the two delegates into a new delegate that points to both methods.

#373 – A Delegate Can Refer to More than One Method

An instance of a delegate can refer to a method.  In C#, delegate types created using the delegate keyword are multicast–they can refer to more than one method at the same time.

In the example below, we define a new delegate type, StringHandlerDelegate, that can refer to a method that has a single string parameter and no return value.  We then declare an instance of that delegate and set it to refer to the Method1 method.  Finally, we use the += operator to indicate that the delegate instance should also refer to the Method2 method.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            StringHandlerDelegate del = Method1;

            del += Method2;

            // Invoke via the delegate--both methods are called
            del("Snidely Whiplash");
        }

        static void Method1(string text)
        {
            Console.WriteLine(text);
        }

        static void Method2(string name)
        {
            Console.WriteLine("Your name is {0}", name);
        }

When we invoke the delegate, both methods are called.

#372 – What Delegates Are Good For

Creating an instance of a delegate that allows you to invoke a method through the delegate doesn’t seem to add much value.  Why not just invoke the method directly?

            // New delegate instance
            LoggerDelegate del1 = Method1;

            // Can still invoke the method directly
            Method1("Direct");

            // Or can invoke through the delegate instance
            del1("Via delegate");

A delegate allows us to treat the reference to the method as a real object, storing it in a variable or passing it to a method as a parameter.  This allows great flexibility, because a method can be told at runtime which child method to call.

        static void DoSomeCalculations(LoggerDelegate log)
        {
            // Do some stuff

            log("I did stuff");

            // Do other stuff

            log("And I did some other stuff");
        }

You then pass in the specific logging method when you invoke DoSomeCalculations and it will call that method.

            DoSomeCalculations(Method1);

#371 – Delegate Basics

In C#, a delegate allows you to create an object that refers to a method.  This object can be thought of as a sort of pointer to the method.

You use the delegate keyword to define a type that can be used to create objects that point to methods having a particular signature.

private delegate void StringHandlerDelegate(string s);

In this example, we define a new delegate type that knows how to point to methods that take a single parameter, of type string, and whose return type is void.

The term delegate refers to this new delegate type.  We can now create an instance of the delegate that points to a specific method.

    static void Main()
    {
        StringHandlerDelegate del1 = Method1;
    }

    static void Method1(string text)
    {
        Console.WriteLine(text);
    }

We can now invoke the method through the delegate instance.

    del1("Invoked thru delegate");

#370 – Subscribe to an Event by Adding an Event Handler

You subscribe to a particular event in C# by defining an event handler–code that will be called whenever the event occurs (is raised). You then attach your event handler to an event on a specific object, using the += operator.

Below is an example where we define an event handler for the Dog.Barked event.  Each time that Kirby barks, we’ll record the date and time of the bark in a list.

        private static List<DateTime> barkLog = new List<DateTime>();

        static void Main()
        {
            Dog kirby = new Dog("Kirby", 12);

            kirby.Barked += new EventHandler(kirby_Barked);

            kirby.Bark();
            Console.ReadLine();

            kirby.Bark();
            Console.ReadLine();
        }

        // Neither argument is used, for the moment
        static void kirby_Barked(object sender, EventArgs e)
        {
            // Log Kirby's barks
            barkLog.Add(DateTime.Now);
        }

Assuming that the Dog class fires its Barked event whenever we call the Bark method, our handler will get called whenever Kirby barks.

#369 – Multiple Clients Can Subscribe to the Same Event

An event in C# is a mechanism that allows an object to inform client code about something that happened inside the object that the other code might care about.

It’s often the case that there are several different pieces of client code that care about the same event.  In other words, when an object raises an event, there may be one subscriber listening for the event, no subscribers, or many subscribers.

Here’s an example that shows two subscribers to the same Dog.Barked event.