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.

Expression Lambdas

Unlike a statement lambda, which includes a statement block and, therefore, zero or more statements, an expression lambda has only an expression, with no statement block. Listing 12.18 is the same as Listing 12.14, except that it uses an expression lambda rather than a statement lambda. Listing 12.18: Passing a Delegate with a Statement Lambda

    class DelegateSample
    {
        // ...
        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]);
            }
        }
    }

The difference between a statement and an expression lambda is that the statement lambda has a statement block on the right side of the lambda operator, whereas the expression lambda has only an expression (no return statement or curly braces, for example).

Generally, you would read a lambda operator in an expression lambda in the same way you would a statement lambda: "go/goes to." In addition, "becomes" is sometimes clearer. In cases such as the BubbleSort() call, where the expression lambda specified is a predicate (returns a Boolean), it is frequently clearer to replace the lambda operator with "such that." This changes the pronunciation of the statement lambda in Listing 12.18 to read "first and second such that first is less than second." One of the most common places for a predicate to appear is in the call to System.Linq.Enumerable()' s Where() function. In cases such as this, neither "such that" nor "goes to" is needed. We would read names.Where(name => name.Contains(" ")) as "names where names dot Contains a space," for example.

One pronunciation difference between the lambda operator in statement lambdas and in expression lambdas is that "such that" terminology applies more to expression lambdas than to statements lambda since the latter tend to be more complex.

The anonymous function does not have any intrinsic type associated with it, although implicit conversion is possible for any delegate type as long as the parameters and return type are compatible. In other words, an anonymous method is no more a ComparisonHandler type than another delegate type such as LessThanHandler. As a result, you cannot use the typeof() operator (see Chapter 17) on an anonymous method, and calling GetType() is possible only after assigning or casting the anonymous method to a delegate variable.Table 12.1 contains additional lambda expression characteristics.

TABLE 12.1: Lambda Expression Notes and Examples
 

Statement Example
Lambda expressions themselves do not have type. In fact, there is no concept of a lambda expression in the CLR. Therefore, there are no members to call directly from a lambda expression. The . operator on a lambda expression will not compile, eliminating even the option of calls to object methods.

// ERROR: Operator '.' cannot be applied to
// operand of type
'lambda expression'
Type type = ((int
x) => x).ToString(); ;

Given that a lambda expression does not have an intrinsic type, it cannot appear on the right of an is operator.

// ERROR: The first operand of an 'is' or 'as'
// operator may not be a lambda expression or
// anonymous method
bool boolean = ((int x) => x) is Func<int, int>;
        }

Although there is no type on the lambda expression on its own, once assigned or cast, the lambda expression takes on a type. Therefore, it is common for developers to informally refer to the type of the lambda expression concerning type compatibility, for example.

// ERROR: Lambda expression is not compatible with
// Func<int, bool> type.
Func<int, bool> expression = ((int x) => x);

A lambda expression cannot be assigned to an implicitly typed local variable since the compiler does not know what type to make the variable given that lambda expressions do not have type.

// ERROR: Cannot assign lambda expression to an
// implicitly typed local variable
var thing = (x => x);

C# does not allow jump statements (break, goto, continue) inside anonymous functions if the target is outside the lambda expression. Similarly, you cannot target a jump statement from outside the lambda expression (or anonymous methods) into the lambda expression.

// ERROR: Control cannot leave the body of an
// anonymous method or lambda
expression
string[] args;
Func<string> expression;
switch(args[0])
{
case "/File":
expression = () =>
{
if (!File.Exists(args[1]))
{
break;
}
// ...
return args[1];
};
// ...
}

Variables introduced within a lambda expression are visible only within the scope of the lambda expression body.

// ERROR: The name 'first' does not
// exist in the current context
Func<int, int, bool> expression =
(first, second) => first > second;
first++;
    }

The compiler's flow analysis is unable to detect initialization of local variables in lambda expressions.

int number;
Func<string, bool> expression =
text => int.TryParse(text, out number);
if (expression("1"))
{
// ERROR: Use of unassigned local variable
System.Console.Write(number);
}
int number;
Func<int, bool> isFortyTwo =
x => 42 == (number = x);
if (isFortyTwo(42))
{
// ERROR: Use of unassigned
local variable
System.Console.Write(number);
}

Total Pages : 9 56789

comments