C #3.0 new features of the language Lambda expression (zhuan)

Source: Internet
Author: User

The anonymous method is introduced in C #2.0, which can be replaced by "inline" code when a delegate is expected. Although the anonymous method provides a lot of expression capabilities in functional programming languages, the syntax of the anonymous method is too arrogant and unnatural. Lambda expressions provide a simpler and more functional syntax for writing anonymous methods.

In fact, Lambda expressions are essentially anonymous methods, that is, when compiling our program code, the compiler will automatically help us convert Lambda expressions to anonymous methods.

4.1.5.1 create a Lambda expression

Lambda expressions are written in the following way:A parameter list is followed by a "=>" mark, followed by an expression or a statement BlockThat is, the syntax format of Lambda expressions is:

Parameter column => statement or statement Block

An example of a Lambda expression is as follows:

delegate int del(int i);
...
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25

For "parameter columns ",The parameter column of a Lambda expression can be of an explicit or implicit type. In a list of parameters with explicit types, each parameter type is explicitly declared. In a list of parameters with implicit types, the type of the parameter is inferred from the context in which the Lambda expression appears.-- Specifically, when a Lambda expression is converted to a compatible delegate type, the delegate type provides the parameter type.

When a Lambda expression has only one parameter of the implicit type, brackets in the parameter list can be omitted. That is:

(param) => expr

Can be abbreviated:

param => expr

Finally, the parameter column can contain any parameter (corresponding to the delegate). If there are 0 or more parameters in the parameter column, you must enclose the parameter column with brackets, as shown below:

() => Console. Write ("0 Parameters ");

 

I => Console. Write ("parentheses can be omitted in the parameter column when one parameter is set; Value: {0}", I );

(X, y) => Console. Write ("contains two parameters; Value: {0 }:{ 1}", x, y );

If there is only one statement in the "statement or statement block", you do not need to enclose it in braces; otherwise, you must use braces, as shown below:

I => Console. Write ("only one statement ");

I =>{ Console. Write ("using braces expression ");};

// Braces are required for two statements.

I =>{ I; Console. Write ("two statements ");};

If "statement or statement block" has a return value, if there is only one statement, the "return" statement can be left blank and the compiler will automatically process it. Otherwise, the following example must be added:

class Test
{
delegate int AddHandler(int x, int y);
static void Print(AddHandler add)
{
Console.Write(add(1, 3));
}
static void Main()
{
Print((x, y) => x y);
Print((x, y) => { int v = x * 10; return y v; });
Console.Read();
}
}

Lambda expressions are delegate implementation methods, so they must follow the following rules:

L The number of parameters of a Lambda expression must be the same as that of a delegate;

L if the delegate parameter contains a ref or out modifier, the Lambda expression parameter column must also contain a modifier;

 

Let's look at the following example:

class Test
{
delegate void OutHandler(out int x);
static void Print(OutHandler test)
{
int i;
test(out i);
Console.Write(i);
}
static void Main()
{
Print((out int x) => x = 3);
Console.Read();
}
}

L if the delegate has a return type, the Lambda expression statement or statement block must also return data of the same type;

L if the delegate has several data type formats and the compiler cannot infer a specific data type in Lambda expressions, you must manually specify the data type.

As shown above, the anonymous method specification mentioned in C #2.0 applies to Lambda expressions. Lambda expressions are supersets of anonymous methods on function lines. They provide the following additional functions:

L Lambda expressions allow parameter types to be omitted and inferred, while anonymous methods require parameter types to be explicitly declared.

L The Lambda expression body can be an expression or statement block, while the anonymous method body can only be a statement block.

L when type parameter derivation and method overload selection, Lambda expressions can be passed as parameters.

L a Lambda expression that uses an expression as the expression body can be converted into an Expression Tree.

4.1.5.2 Lambda expression Conversion

Similar to an anonymous method expression, a Lambda expression can be classified as a value with a specific conversion rule. This value has no type, but can be implicitly converted to a compatible delegate type. In particular, delegate type D is compatible with Lambda expression L when the following conditions are met:

 

L D and L have the same number of parameters;

L if L has an explicit parameter list, the type and modifier of each parameter in D must be exactly the same as the corresponding parameter in L;

L if L has a list of implicit parameters, no ref or out parameter is available in D;

L if D has the void return value type and the expression body of L is an expression, if the type of each parameter of L is the same as that of D, then the expression body of L must be a valid expression that can be accepted as statement-expression;

L if D has the void return value type and the expression body of L is a statement block, if the type of each parameter of L is the same as that of D, then the expression body of L must be a valid statement block, and the statement block cannot contain a return statement with an expression;

L if the return value type of D is not void and the expression body of L is an expression, if the type of each parameter of L is the same as that of D, then the expression body of L must be a valid expression that can be implicitly converted to the return value type of D;

L if the return value type of D is not void and the expression body of L is a statement block, if the type of each parameter of L is the same as that of D, then the expression body of L must be a valid statement block, which cannot have an reachable end point (that is, a return statement must exist ), in addition, the expressions in each return statement must be implicitly converted to the return value type of D.

In the following example, a model is used to delegate F <U, T> to a function. It has a U parameter and the return value type is T:

delegate T F<U, T>(U u);

We can assign values as follows:

F<int, int> f1 = x => x 1;
F<int, double> f2 = x => x 1;

The parameter and return value types of each Lambda expression are detected by the type of the variable assigned by the Lambda expression. The first assignment successfully converts the Lambda expression to the delegate type Func <int, int>, because the x type is int, and x 1 is a valid expression, and can be implicitly converted to int. Similarly, the second assignment successfully converts the Lambda expression to the delegate type Func <int, double>, because the result of x 1 (whose type is int) can be implicitly converted to the double type.

 

Let's look at the following code. What if the value is assigned like this?

F<double, int> f3 = x => x 1;

When we run the above Code, the compiler will report the following two errors:

(1) The type "double" cannot be implicitly converted to "int ". There is an explicit conversion (is forced conversion missing ?).

(2) The Lambda expression cannot be converted to the delegate type "F <double, int>" because some return types in the block cannot be implicitly converted to the delegate return type.

In fact, a compilation error occurs because the given type of x is double and the result of x 1 (Type: double) cannot be implicitly converted to int.

20.5.3 type inference

When a model method is called without specifying a type parameter, a type inference process attempts to deduce the type parameter for this call. The Lambda expression passed as a parameter to the model method will also participate in this type inference process.

The first type inference is independent of all parameters. In this initial stage, nothing is inferred from the Lambda expression as a parameter. However, after the initial stage, it will be inferred from the Lambda expression through an iteration process. In particular, the inference is completed when one of the following conditions is true:

The l parameter is a Lambda expression (hereinafter referred to as L), which is not inferred from it;

L The type of the corresponding parameter, hereinafter referred to as P, is a delegate type. The return value type includes one or more method type parameters;

L P and L have the same number of parameters. The modifier of each parameter in P is the same as that in L, or if L has an implicit parameter list, no parameter modifier;

L The parameter type of P does not contain method type parameters, or only contains a set of compatible type parameters that have been inferred;

L if L has an explicit parameter list, when the inferred type is replaced by the method type parameter in P, each parameter in P should be of the same type as the corresponding parameter in L.

 

L if L has a list of parameters of the implicit type, when the inferred type is replaced by the method type parameter in P and the parameter type as the result is assigned to L, the expression body of L must be a valid expression or statement block.

L can infer a return value type for L.

For each such parameter, It is inferred by associating the return value type of P and the return value type inferred from L, and the new inference will be added to the cumulative inference set. This process is repeated until more inferences are made.

In type inference and overload selection, the Lambda expression L's "inferred return value type" can be checked using the following steps:

L if the expression body of L is an expression, the type of the expression is the type of the returned value inferred by L.

L if the expression body of L is a statement block, if the set of expressions in the return statement in the block exactly contains a type, so that each type in the set can be implicitly converted to this type, And this type is not an empty type, then this type is the type of the inferred return value of L.

L In addition, a return value type cannot be inferred from L.

For an example of type inference that contains a Lambda expression, consider the Select Extension Method declared in the System. Query. Sequence class:

namespace System.Query
{
public static class Sequence
{
public static IEnumerable<S> Select<T, S>(
this IEnumerable<T> source,
Func<T, S> selector)
{
foreach (T element in source) yield return selector(element);
}
}
}

 

Assume that the System. Query namespace is imported using the using statement and a Customer class is defined with a string-type attribute Name. The Select method can be used to Select a Name from a Customer list:

List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);

The call to the Select extension method will be processed as a static method call:

IEnumerable<string> names = Sequence.Select(customers, c => c.Name);

Because no type parameter is explicitly specified, the type parameter is derived through type inference. First, the customers parameter is associated with the source parameter, and T is inferred to be the Customer. Then, we use the lambda expression type inference process mentioned above. The C type is Customer, and the expression c. Name will be associated with the return value type of the selector parameter. Therefore, we can infer that S is string. Therefore, this call is equivalent:

Sequence.Select<Customer, string>(customers, (Customer c) => c.Name);

And the return value type is IEnumerable <string>.

The following example demonstrates how the type inference of a Lambda expression allows the type information to flow between parameters called by a normal method. For the given method:

static Z F<X, Y, Z>(X value, Func<X, Y> f1, Func<Y, Z> f2) { return f2(f1(value)); }

Now let's write such a call to see its inference process:

double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => TotalSeconds);

The type inference process is as follows: first, the parameter "1:15:30" is associated with the value parameter, and X is inferred to be a string. Then, the parameter s of the first Lambda expression has a string of the inferred type. The Return Value Type of the expression TimeSpan. Parse (s) is associated with f1, and Y is System. TimeSpan. Finally, the parameter t of the second Lambda expression has the inferred type System. TimeSpan, and the expression t. TotalSeconds is associated with the return value type of f2. It is inferred that Z is double. Therefore, the result type of this call is double.

 

Optional 5.4 heavy load Selection

The Lambda expression in the parameter list will affect the heavy-load Selection in specific circumstances (also, load-Load Analysis and heavy-load resolution, that is, the process of selecting the most appropriate method from several heavy-load methods for calling ).

The following are the newly added rules:

For Lambda expressions L with the inferred return value type, when the delegate type D1 and delegate type D2 have the same parameter list, in addition, if the implicit conversion of the inferred Return Value Type of L to the return value type of D1 is better than the implicit conversion of the inferred Return Value Type of L to the return value type of D2, implicit conversions from L to D1 are better than those from L to D2. If none of these conditions are true, both conversions are not optimal.

Limit 5.5 Expression Tree

The Expression Tree allows Lambda expressions to be represented as data structures rather than executable code. A Lambda Expression that can be converted to the delegate type D, or an Expression Tree of the System. Linq. Expressions. Expression <D> type. Converting a Lambda expression to a delegate type causes the executable code to be generated and referenced by the delegate, and converting it to an Expression Tree Type causes the code that has created an Expression Tree instance to be issued. The Expression Tree is an efficient data representation in the memory of Lambda expressions, and makes the expression structure transparent and obvious.

The following example shows a Lambda expression as an executable code and an Expression Tree. Because there is a conversion to Func <int, int>, there is a conversion to Expression <Func <int, int>. The Code is as follows:

Using System. Linq. Expressions;
// Code
Func <int, int> f = x => x 1;
// Data
Expression <Func <int, int> e = x => x 1;

After the assignment is complete, delegate f to identify a method that returns x 1, while Expression Tree e represents a data structure that describes expression x 1.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.