#1,151 – The Func Delegate Type

The .NET Framework includes the Func predefined delegate type, which represents the signature of a method that returns some type and accepts zero or more parameters.

Overloads of Func are :

  • delegate TResult Func<out TResult>()
  • delegate TResult Func<in T, out TResult>(T p1)
  • delegate TResult Func<in T1, in T2, out TResutl>(T1 p1, T2 p2)
  • etc…

You might define your own delegate type:

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

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

However, you could instead use the Func delegate type, avoiding the definition of your own custom delegate type.

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

Func<T,T,T> indicates that we’ll pass in a function that takes two parameters of a given type and returns a value of the same type.

Advertisements

#1,149 – Generic Delegate Types

A delegate type can be generic.  Consider the following delegate type:

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

This delegate type defines a pattern for methods that take two parameters of a particular type and returns the same type.  Below are a couple examples of methods whose signature matches this delegate type.  (Methods are only static so that we can call them from another static method).

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

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

We can now declare Merger<T> instances that refer to these methods as follows:

            Merger<int> adder = Add;
            Merger<Dog> breeder = Breed;

#1,022 – How to Use Return Values from All Delegate Instances

If a delegate instance has an invocation list with multiple methods and you invoke the delegate using the standard functional notation, you’ll get back the return value only for the last method in the invocation list.

If you want to consume the return value for each method in a delegate’s invocation list, you can use GetInvocationList to explicitly iterate through the invocation list.

            ReturnIntDelegate del1 = Method1; // Returns 12
            del1 += Method2;  // Returns 99

            // Invoke all at once, get return value
            // only from last
            Console.WriteLine("Invoke normally");
            int val = del1();
            Console.WriteLine(val);

            // Invoke one at a time
            Console.WriteLine("Invoke one at a time");
            foreach (ReturnIntDelegate del in del1.GetInvocationList())
            {
                int ret = del.Invoke();
                Console.WriteLine(ret);
            }

1022-001

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