FREE BOOK

Chapter 12 - Delegates and Lambda Expressions

Posted by Addison Wesley Free Book | LINQ October 13, 2009
C# achieves the same functionality using a delegate, which encapsulates methods as objects, enabling an indirect method call bound at runtime.

Anonymous Methods

C# 2.0 and above include a feature known as anonymous methods. These are delegate instances with no actual method declaration. Instead, they are defined inline in the code, as shown in Listing 12.11.

Listing 12.11: Passing an Anonymous Method

class DelegateSample
{
// ...
BubbleSort(items, AlphabeticalGreaterThan);
}

OUTPUT 12.1:

Enter an integer: 1
Enter an integer: 12
Enter an integer: 13
Enter an integer: 5
Enter an integer: 4
1
12
13
4
5

static void Main(string[] args)
{
int i;
int[] items = new int[5];
for (i=0; i<items.Length; i++)
{
Console.Write("Enter an integer:");
items[i] = int.Parse(Console.ReadLine());
}
for (i = 0; i < items.Length; i++)
{
Console.WriteLine(items[i]);
}
}
}

In Listing 12.11, you change the call to BubbleSort() to use an anonymous method that sorts items in descending order. Notice that no LessThan() method is specified. Instead, the delegate keyword is placed directly inline with the code. In this context, the delegate keyword serves as a means of specifying a type of "delegate literal," similar to how quotes specify a string literal.

You can even call the BubbleSort() method directly, without declaring the comparisonMethod variable (see Listing 12.12).

Listing 12.12: Using an Anonymous Method without Declaring a Variable

    class DelegateSample
    {
        // ...
        static void Main(string[] args)
{
ComparisonHandler comparisonMethod;
comparisonMethod =
delegate(int first, int second)
{
return first < second;
};
BubbleSort(items, comparisonMethod);
int i;
int[] items = new int[5];
for (i=0; i<items.Length; i++)
{
Console.Write("Enter an integer:");
items[i] = int.Parse(Console.ReadLine());
}
for (i = 0; i < items.Length; i++)
{
Console.WriteLine(items[i]);
}
}
    }

Note that in all cases, the parameter types and the return type must be compatible with the ComparisonHandler data type, the delegate type of the second parameter of BubbleSort().

In summary, C# 2.0 included a new feature, anonymous methods, that provided a means to declare a method with no name and convert it into a delegate.

Parameterless Anonymous Methods

Compatibility of the method signature with the delegate data type does not exclude the possibility of no parameter list. Unlike with lambda expressions, statement lambdas, and expression lambdas (see the next section), anonymous methods are allowed to omit the parameter list (delegate { return Console.ReadLine() != ""}, for example). This is atypical, but it does allow the same anonymous method to appear in multiple scenarios even though the delegate type may vary. Note, however, that although the parameter list may be omitted, the return type will still need to be compatible with that of the delegate (unless an exception is thrown).

BubbleSort(items,delegate(int first, int second)
{
return first < second;}
);

In .NET 3.5 (C# 3.0), there exists a series of generic delegates with the name "Func." The signatures for these delegates are shown in Listing 12.13.

Listing 12.13: Func Delegate Declarations

public delegate TResult Func<TResult>();
public delegate TResult Func<T, TResult>(T arg)
public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2)
public delegate TResult Func<T1, T2, T3, TResult>(
T1 arg1, T2 arg2, T3 arg3)
public delegate TResult Func<T1, T2, T3, T4, TResult>(
T1 arg1, T2 arg2, T3 arg3, T4 arg4)

Since these delegate definitions are generic, it is possible to use them instead of defining a custom delegate. For example, rather than declaring the ComparisonHandler delegate type, code could simply declare Comparison- Handler delegates using Func<int, int, bool>. The last type parameter of Func is always the return type of the delegate. The earlier type parameters correspond in sequence to the type of delegate parameters. In the case of ComparisonHandler, the return is bool (the last type parameter of the Func declaration) and the type arguments int and int correspond with the first and second parameters of ComparisonHandler. In many cases, the inclusion of Func delegates into the .NET 3.5 Framework eliminates the necessity to define delegates with four or fewer parameters that return a value. (You should use System.Action for delegates that have no return and that take no parameters.) However, you should still declare delegate types when a specific delegate type would simplify coding with the delegate. For example, continuing to use the ComparisonHandler provides a more explicit indication of what the delegate is used for, whereas Func<int, int, bool> provides nothing more than an understanding of the method signature. Evaluation about whether to declare a delegate is still meaningful and includes considerations such as whether the name of the delegate identifier is sufficient for indicating intent, whether the delegate type name would clarify its use, and whether the use of a .NET 3.5 type will limit the use of the assembly to .NET 3.5 clients unnecessarily.

Note that even though you can use a Func generic delegate in place of an explicitly defined delegate, the types are not compatible. You cannot assign any expression of one delegate type to a variable of another delegate type. For example, you cannot assign a ComparisonHandler variable to a Func<int, int, bool> variable or pass them interchangeably as parameters.

Total Pages : 9 34567

comments