#1,021 – What Happens to a Delegate’s Return Value

The return value from a delegate instance is passed back to the caller.  For example:

        private delegate int ReturnIntDelegate();

        static int Method1()
        {
            return 12;
        }

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

            // Invoke
            int val = del1();
            Console.WriteLine(val);
        }

1021-001

Recall, however, that a delegate instance can refer to more than one method.  When a caller invokes a delegate whose invocation list contains more than one method, the value returned to the caller is the return value of the last delegate instance in the invocation list.  Return values of other methods in the invocation list are lost.

        static int Method1()
        {
            return 12;
        }

        static int Method2()
        {
            return 99;
        }

        static void Main(string[] args)
        {
            ReturnIntDelegate del1 = Method1;
            del1 += Method2;

            // Invoke
            int val = del1();
            Console.WriteLine(val);
        }

1021-002

#1,020 – Removing Methods from a Delegate’s Invocation List

You can use the += syntax to add methods to a delegate’s invocation list and then -= syntax to remove methods from its invocation list.  In either case, you add or remove methods that are included in a second delegate’s invocation list from the first delegate’s invocation list.

In the example below, we remove del2’s invocation list (Method2, Method3) from del1’s invocation list (Method1, Method2, Method3, Method4, Method5).  del1’s invocation list then becomes: Method1, Method4, Method5.

            // del1 invokes 5 methods
            StringHandlerDelegate del1 = Method1;
            del1 += Method2;
            del1 += Method3;
            del1 += Method4;
            del1 += Method5;
            del1("del1 Howdy");
            Console.WriteLine();

            // del2 invokes 2 methods
            StringHandlerDelegate del2 = Method2;
            del2 += Method3;
            del2("del2 Howdy");
            Console.WriteLine();

            // Remove del2's methos from del1's invocation list
            del1 -= del2;
            del1("del1 Howdy again");

1020-001
When removing more than one method, the invocation list to be removed must be a contiguous subset of the larger invocation list.  (E.g. {Method2, Method3} rather than {Method2, Method4}).

#1,019 – Syntax for Adding Methods to a Delegate’s Invocation List

If you have a delegate instance, it will have zero or more methods in its invocation list.  When you invoke the delegate instance, these are the methods that will be called.

For a given delegate instance, you can add or remove methods to its invocation list.  In actuality, since a delegate is immutable, you’re actually creating a new delegate instance that has the specified methods added or removed.

Below are several examples of how you might add a new method to the invocation list of an existing delegate instance.

            // Add Method2 to original invocation list
            StringHandlerDelegate del1 = Method1;
            del1 += Method2;
            del1("Howdy 1");

            // Does the same thing
            del1 = Method1;
            del1 = del1 + Method2;
            del1("Howdy 2");

            // Does the same thing
            del1 = Method1;
            del1 = (StringHandlerDelegate)Delegate.Combine(del1, new StringHandlerDelegate(Method2));
            del1("Howdy 3");

1019-001

#1,017 – Delegate Types vs. Delegate Instances

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

delegate type defines a signature for a method (number and type of parameters and return type).  Below, StringHandlerDelegate is a delegate type.

private delegate void StringHandlerDelegate(string s);

delegate instance is an instance of a delegate type that can refer to one or more methods.  Below, del1 is a delegate instance.

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

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

Technically, the term delegate refers to the delegate type, rather than the delegate instance.  But to avoid confusion, you could always refer explicitly to either the delegate instance or the delegate type.

#554 – Rules for Matching an Anonymous Method to a Delegate Type

When you declare an anonymous method and assign it to a delegate type or pass it as a delegate-typed parameter, the anonymous method is converted to an instance of the appropriate delegate.  There are several rules that you must follow so that this conversion can happen properly.

  • You can only omit the formal parameter list in the anonymous method declaration if
    • The delegate type’s parameter list has no ref or out parameters
    • The body of the anonymous method does not need to read or write any of the parameters
  • If you include the formal parameter list in the anonymous method declaration, you must include all of the parameters and the type must match for each parameter
  • Any object returned as the return value of the anonymous method must be able to be implicitly converted to the return type of the delegate

#552 – Anonymous Methods Have Access to Local Variables Where They Are Declared

When you declare an anonymous method, the body of the method has access to any formal parameter that you declared as part of the anonymous method declaration.  For example, in the code fragment before, the body of the method has access to the s parameter.

StringHandlerDelegate del1 =
    delegate (string s) { Console.WriteLine(s); };

The body of an anonymous method can also make use of any variables visible within the scope where it is defined. In the example below, the method makes use of the local variable rudeNeighbor.  The Bark method accepts a delegate and then invokes that delegate, passing back the actual bark noise that the dog makes.

        static void Main()
        {
            string rudeNeighbor = "Paul";

            Dog d = new Dog("Jack", 17);

            d.Bark(delegate(string s) { Console.WriteLine("Hey {0}: {1}", rudeNeighbor, s); });
        }

For completeness, here is the body of the Dog.Bark method:

        public void Bark(BarkAtDelegate barkMethod)
        {
            barkMethod("Grrrr");
        }

#550 – Anonymous Method Does Not Require Formal Parameters

An anonymous method declares the body of code to execute for a delegate inline with the declaration of the delegate instance.

In the example below, the delegate type takes a single string parameter and we include this parameter in our declaration of the anonymous method.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            StringHandlerDelegate del1 =
                delegate (string s) { Console.WriteLine(s); };
            del1("Invoked via delegate");

The anonymous method expression shown above includes a list of format parameters, in this case a single string parameter. However, this list of parameters is optional, even if the delegate type includes one or more parameters. In the example below, we define a second anonymous method for the same delegate type, but without any formal parameters.

            StringHandlerDelegate del2 =
                delegate { Console.WriteLine("I'm ignoring my string parameter!"); };
            del2("Ignore me");

#549 – Anonymous Method Basics

Recall that an instance of a delegate is an object that refers to one or more methods.  Code that has access to the delegate  instance can execute the various methods via the delegate.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            StringHandlerDelegate del1 = Method1;
            del1("Invoked via delegate");
        }

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

An anonymous method is a shorthand for declaring a delegate instance that allows declaring the code to execute as part of the delegate’s declaration, rather than in a separate method.

The code below is equivalent to the example above, but using an anonymous method, rather than a separately defined method.

        private delegate void StringHandlerDelegate(string s);

        static void Main()
        {
            StringHandlerDelegate del1 =
                delegate (string s) { Console.WriteLine(s); };
            del1("Invoked via delegate");
        }

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

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