#391 – Passing a Delegate Instance as a Parameter

When you use the delegate keyword to define a delegate, you’re actually defining a new class, which inherits from System.MulticastDelegate.

        public delegate string StringProcessorDelegate(string input);

When you declare a variable of this delegate type and assign a method to it, you’re creating an instance of the new class.

            StringProcessorDelegate stringProcessor = ReverseAndHighLight;

 

        static string ReverseAndHighLight(string input)
        {
            // Reverse, add asterisks
        }

You now have a delegate instance that is set up to invoke the ReverseAndHighlight method.  This instance is actually just a normal .NET object, since it’s an instance of the new type that derives from System.MulticastDelegate.  This means that you can pass this object to a method, as a parameter.

            string result = DoStringProcessing(stringProcessor, "Basil Rathbone");

 

        static string DoStringProcessing(StringProcessorDelegate del, string input)
        {
            Console.WriteLine("Input string: {0}", input);
            string result = del(input);
            Console.WriteLine("Result: {0}", result);

            return result;
        }

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

#385 – A Delegate Instance Can Refer to Both Instance and Static Methods

A delegate instance in C# can refer to one or more methods that all have a particular signature.  You can assign a single method to the delegate using the assignment (=) operator.  You can also add or remove methods from the delegate’s invocation list using the addition assignment (+=) and subtraction assignment (-=) operators.

The delegate can refer to both instance and static methods.  Internally, the delegate will store a reference to either a static method or to an instance method as well as the specific object to invoke the instance method on.

        public delegate void StringDelegate(string s);

        static void Main()
        {
            // First method on invocation list--instance method
            Dog kirby = new Dog("Kirby");
            StringDelegate del = kirby.Bark;

            // 2nd method on invocation list--static method
            del += LogABark;

            // Invoke methods on invocation list--both static and instance methods in this case
            del("Growf!");
        }

        static void LogABark(string bark)
        {
            Console.WriteLine("Logging bark [{0}]", 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;

#382 – Handling an Event that Returns Some Data

When you implement an event that returns data, you can use the EventHandler<TEventArgs> delegate type.

When you use the EventHandler<TEventArgs> delegate type, you define a new class that inherits from EventArgs.  A client that wants to handle the new event will need access to this new class.

Suppose that a Dog class implements a Barked event that has the following signature:

        public event EventHandler<BarkedEventArgs> Barked;

You would then add an event handler that accepts two parameters:

  • Parameter of type object, which references the object that raised the event
  • Parameter of type BarkedEventArgs, which contains the data the event wants to pass back

Here’s the complete example:

        static void Main()
        {
            Dog kirby = new Dog("Kirby");
            kirby.Barked += kirby_Barked;

            kirby.Bark("Grrr");
        }

        // Handle the Barked event
        static void kirby_Barked(object o, BarkedEventArgs e)
        {
            Console.WriteLine("Kirby barked: {0}", e.BarkSound);
        }