#1,205 – C# 6.0 – Using the Null-Conditional when Invoking a Delegate

When you fire an event (by invoking a delegate), you typically need to check for null before invocation.  This avoids a null reference exception if there are no subscribers to the event.

    public class Dog
    {
        public Dog(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public event EventHandler<string> NameChange;

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                if (value != name)
                {
                    name = value;

                    // Check for null before invocation
                    if (NameChange != null)
                    {
                        NameChange(this, name);
                    }
                }
            }
        }

        public int Age { get; set; }
    }

(The above pattern is not thread-safe).

An alternative pattern is to declare the event with a null handler, so that there is always at least one handler on the invocation list. (This is thread-safe).

In C# 6.0, we can use the null-conditional to invoke a delegate.

                    NameChange?.Invoke(this, name);
Advertisement

#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,150 – Generic Delegate Type as Parameter

You can use a generic delegate type as a parameter to a method.

Suppose that we have the following type:

        public delegate T Merger<T>(T thing1, T thing2);

We can now create a method that takes a constructed version of this type as a parameter.  We could then call the method as shown in the Main method.

        static void MergeInts(Merger<int> intMerger, int n1, int n2)
        {
            int result = intMerger(n1, n2);
        }

        public static int Add(int n1, int n2)
        {
            return n1 + n2;
        }

        static void Main(string[] args)
        {
            MergeInts(Add, 5, 6);
        }

We could also define a generic method that has a generic delegate parameter.

        static void MergeSomething<T>(Merger<T> someMerger, T thing1, T thing2)
        {
            T result = someMerger(thing1, thing2);
        }

We can then call this method as follows:

        public static Dog Breed(Dog mama, Dog papa)
        {
                return new Dog(mama.Name + papa.Name);
        }

        static void Main(string[] args)
        {
            MergeSomething(Add, 5, 6);

            Dog dog1 = new Dog("Lassie");
            Dog dog2 = new Dog("Spuds");
            MergeSomething(Breed, dog1, dog2);
        }

#1,018 – Delegate Invocation Syntax

In C#, you typically invoke a delegate by using the variable referring to the delegate instance as if it were a method.

For example, in the code below, the variable del1 is a delegate instance and you invoke it by using del1 as if it was a method.

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

        static void Main(string[] args)
        {
            StringHandlerDelegate del1 = Method1;

            // Invoke the delegate instance
            del1("Invoked through delegate");

            Console.ReadLine();
        }

Alternatively, you can call the Invoke method on the delegate instance.  This does exactly the same thing as the example above.

            // Invocation, method #2
            del1.Invoke("Also invoked thru delegate");

#637 – A Delegate Can Refer to A Partial Method

You can have a delegate refer to a partial method, but only if the partial method is implemented.  At compile-time, if a delegate points to a partial method and you’re compiling only the declaration of the partial method, but not an implementation, you’ll get a compiler error.

    // Dog-1.cs
    public partial class Dog
    {
        // Action is a delegate type in System that takes
        //   no parameters and returns a void
        Action barkDelegate;

        public Dog()
        {
            // Point delegate to partial method Bark.
            barkDelegate = Bark;
        }

        public void BarkViaDelegate()
        {
            barkDelegate();
        }

        // Partial method declaration--no implementation here
        partial void Bark();
    }
    // Dog-2.cs
    public partial class Dog
    {
        // Implementation of Bark
        partial void Bark()
        {
            Console.WriteLine("Woof!");
        }
    }
        static void Main()
        {
            Dog d = new Dog();
            d.BarkViaDelegate();
        }

#626 – Nested Type Options

When you declare one type inside of another, the outer type must be either a class or a struct.  The inner (nested) type can be one of the following: class, struct, interface, delegate or enum.

Here are a few common examples.

A struct in a class:

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

        public struct DogCollar
        {
            public int Length;
            public string Material;
        }
    }

A delegate type  defined in a class:

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

        public delegate void BarkHandler(object sender, BarkEventArgs e);
    }

An enum in a class:

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

        public enum Temperament { Docile, Excitable, Vicious };
    }

#551 – Use Anonymous Method When Adding to an Invocation List

Recall that an instance of a delegate can refer to more than one method (delegates are multicast).  In addition to using an anonymous method to declare a new instance of a delegate, you can also use anonymous methods when adding a new method to an existing delegate instance.  (Adding to the delegate instance’s invocation list).

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            // Declare new delegate instance, with anonymous
            // method in its invocation list
            StringHandlerDelegate del1 =
                delegate (string s) { Console.WriteLine(s); };

            // Now add a 2nd method to delegate, also using
            // anonymous method
            del1 +=
                delegate(string s) { Console.WriteLine(new string(s.Reverse().ToArray())); };

            // When we invoke the delegate, both methods are
            // called
            del1("Mairzy Doats and Dozy Doats");
        }

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

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