#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.

#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);