C # 3.0 Language new features (language Specification): 3 lambda expressions

Source: Internet
Author: User
Tags foreach anonymous contains expression modifier new features static class valid
Specification

Original: "C # Version 3.0 Specification", Microsoft
Translation: Lover_p
In C # 2.0, an anonymous method is introduced, allowing for "inline (in-line)" code to be substituted when a delegate is expected to appear. Although anonymous methods provide a lot of expressive power in functional programming languages, the syntax of anonymous methods is too verbose and unnatural. The lambda expression (Lambda expressions) provides a simpler, more functional syntax for writing anonymous methods.

The lambda expression is written by a list of arguments followed by a => notation and followed by an expression or a block of statements.

Expression
Assignment
Non-assignment-expression

Non-assignment-expression:
Conditional-expression
Lambda-expression
Query-expression

Lambda-expression:
(lambda-parameter-listopt) => lambda-expression-body
Implicitly-typed-lambda-parameter => Lambda-expression-body

Lambda-parameter-list:
Explicitly-typed-lambda-parameter-list
Implicitly-typed-lambda-parameter-list

Explicitly-typed-lambda-parameter-list:
Explicitly-typed-lambda-parameter
Explicitly-typed-lambda-parameter-list, Explicitly-typed-lambda-parameter

Explicitly-typed-lambda-parameter:
Parameter-modifieropt type identifier

Implicitly-typed-lambda-parameter-list:
Implicitly-typed-lambda-parameter
Implicitly-typed-lambda-parameter-list, Implicitly-typed-lambda-parameter

Implicitly-typed-lambda-parameter:
Identifier

Lambda-expression-body:
Expression
Block

The parameters of an lambda expression can have an explicit or implicit type. In a parameter list with an explicit type, the type of each parameter is explicitly declared. In an implicitly typed argument list, the type of the parameter is inferred from the context in which the lambda expression appears-specifically, when the lambda expression is converted to a compatible delegate type, the delegate type provides the type of the argument.

When a lambda expression has only one parameter with an implicit type, the parentheses in the argument list can be omitted. In other words, the following form of the lambda expression:

(param) => expr

can be abbreviated as:

param => Expr

Some examples of lambda expressions are given below:

x => x + 1//implicit type, using an expression as the lambda expression body

X => {return x + 1;} An explicit type, with a statement block as the body of the lambda expression

(int x) => x + 1//explicit type, with an expression as the body of the lambda expression

(int x) => {return x + 1;} An explicit type, with a statement block as the body of the lambda expression

(x, y) => x * Y//Multiple parameters

() => Console.WriteLine ()//No parameters

In general, the anonymous method specification mentioned in the C # 2.0 specification also applies to URAMDA expressions. The lambda expression is a superset of the anonymous method on the function line, providing the following additional features:

The L lambda expression allows the argument type to be omitted and inferred, whereas an anonymous method requires that the parameter type must be explicitly declared.

L The lambda expression body can be an expression or a statement block, whereas an anonymous method body can only be a statement block.

L The lambda expression can be passed as a parameter when the type parameter derivation and the method overload choice.

L A lambda expression that takes an expression as a body can be converted to an expression tree.

Attention

The PDC 2005 Technology Preview compiler does not support lambda expressions that use a single statement block as the body of an expression. When a statement block is required, use the anonymous method syntax in C # 2.0.

3.1 Lambda expression conversions

Like an anonymous method expression, a lambda expression can be categorized as a value that has a specific transformation rule. This value has no type, but can be implicitly converted to a compatible delegate type. In particular, the delegate type D is compatible with the URAMDA expression L when the following conditions are met:

L D and L have the same number of parameters.

L if l have an explicit type argument list, the type and modifier for each parameter in D must be exactly the same as the corresponding parameter in L.

L if l have an implicitly typed argument list, there cannot be a ref or out parameter in D.

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

L if D has a void return value type and the expression body of L is a statement block, and if the type of each parameter of L is the same as the parameter of D, then the expression body of L must be a valid statement block, and the statement block cannot have 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 the parameter of D, then the expression body of L must be a valid expression of the return value type that can be implicitly converted to 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 the parameter of D, then the expression body of L must be a valid statement block, the statement block cannot have the end point (that is, must have return statement, translator note), and the expressions in each return statement must be able to be implicitly converted to the returned value type of D.

The following example will use a generic delegate func<a, R>, to represent a function that has a parameter of type A and the return value type R:

Delegate R Func<a, r> (A Arg);

In the following assignment:

Func<int, int> f1 = x => x + 1; Ok

Func<int, double> f2 = x => x + 1; Ok

Func<double, int> f3 = x => x + 1; Error

The parameters and return value types for each lambda expression are detected by the type of the variable that the Ramdada expression is assigned to. The first assignment successfully converts the lambda expression to the delegate type Func<int, Int>, because the type of x is Int,x + 1 is a valid expression and can be implicitly converted to int. Similarly, the second assignment succeeds in converting the lambda expression to the delegate type Func<int, Double>, because the result of X + 1 (type int) can be implicitly converted to the double type. However, the third assignment produces a compile-time error because the result of the given type of x is Double,x + 1 (type Double) cannot be implicitly converted to int.

3.2 Type Inference

When a generic method is called without specifying a type parameter, a type inference process goes back and attempts to infer the type parameter for the call. A lambda expression that is passed as a parameter to a generic method also participates in this type inference process.

The first occurrence of type inference is independent of all parameters. In this initial phase, nothing is inferred from the lambda expression as a parameter. However, after the initial phase, an iterative process is used to infer from the lambda expression. In particular, the inference is complete when one of the following conditions is true:

The L parameter is a lambda expression, hereafter referred to as L, from which no inference is obtained.

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

L P and L have the same number of parameters, the modifiers for each parameter in P are consistent with the corresponding parameters in L, or if l have an implicitly typed argument list, there is no parameter modifier.

The parameter type of L p does not contain a method type parameter or only a set of type parameters that are compatible with the inferred type parameters.

L if l have an explicit type argument list, when the inferred type is replaced by the method type argument in P, each parameter in P should have the same type as the corresponding parameter in L.

L if l have an implicitly typed argument list, the expression body of L must be a valid expression or statement block when the inferred type is replaced by the method type parameter in P and the parameter type of the result is assigned to L.

L can infer a return value type for L. This will be described later.

For each such parameter, it is inferred from the associated P's return value type and the return value type inferred from L, and the new inference is added to the cumulative inference set. This process is repeated until no more inferences can be made.

In type inference and overload selection, the inferred return value type of the lambda expression L is detected by using the following steps:

L If the expression body of L is an expression, then the type of the expression is the inferred return value type of L.

L If the expression body of L is a block of statements, if the collection of the type of the expression in the return statement in the block contains exactly one type, so that each type in the collection can be implicitly converted to that type, and the type is not an empty type, then the type is the inferred return value type of L.

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

As 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);

}

}

}

Suppose the System.query namespace is imported with a using statement, and a customer class is defined, with a property of type string Name,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);

Calls to the extension method select are processed as a static method call:

ienumerable<string> names = Sequence.select (Customers, C => c.name);

The type argument is inferred by type inference because the type parameter is not explicitly specified. First, the customers parameter is associated to the source parameter, and T is inferred to be customer. Then using the lambda expression type inference process mentioned above, the type of C is customer, expression C. Name will be associated to the return value type of the selector parameter, so it is inferred that s is a string. Therefore, this call is equivalent to:

Sequence.select<customer, String> (Customers, (Customer c) => c.name)

and its return value type is ienumerable<string>.

The following example shows how the type inference of a lambda expression allows the type information to "flow" between parameters of a generic method invocation. For a given method:

Static Z f<x, Y, z> (X value, func<x, y> F1, Func<y, z> F2) {

Return F2 (F1 (value));

}

The following call:

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 to the value parameter, and the inference x is string. Then, the argument s of the first lambda expression has the inferred type string, and the expression Timespan.parse (s) is associated to the F1 return value type, which infers that 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 to the F2 return value type, and the inferred Z is double. So the result type of this call is double.

3.3 Overload Choice

The lambda expression in the argument list will affect the overload choice in a particular case (also known as overload analysis, overload resolution, etc., which is the process of selecting the most appropriate method from several overloaded methods, the translator notes).

Here is the newly added rule: for lambda expression L, which has an inferred return value type, when the delegate type D1 and the delegate type D2 have exactly the same argument list, And when the inferred return value type of L is implicitly converted to the return value type of D1, the implicit conversion of L to D1 is better than the implicit conversion of L to D2 when the inferred return value type of L is implicitly converted to the return value type of D2. If none of these conditions are true, then two conversions are not optimal.

The following example explains this rule.

Class Itemlist<t>: list<t>

{

public int sum<t> (func<t, int> selector) {

int sum = 0;

foreach (T item in this) sum + + = selector (item);

return sum;

}

Public double sum<t> (func<t, double> selector) {

Double sum = 0;

foreach (T item in this) sum + + = selector (item);

return sum;

}

}

Itemlist<t> has two sum methods. Each comes with a selector parameter that is used to sum the values from the list items, sorted by selection. The value you select is either int or double, and the result is int or double.

You can use the Sum method to sum an order based on a product schedule:

Class Detail

{

public int unitcount;

public double UnitPrice;

...

}

void Computesums () {

Itemlist<detail> OrderDetails = GetOrderDetails (...);

int totalunits = orderdetails.sum (d => d.unitcount);

Double orderTotal = orderdetails.sum (d => d.unitprice * d.unitcount);

...

}

In the first call to Orderdetails.sum, two sum methods are possible because the lambda expression D => d.unitcount and Func<detail, int> and Func<detail, double > are compatible. However, the overload choice selects the first sum method, because converting to func<detail, int> is better than converting to Func<detail, double>.

In the second call to Orderdetails.sum, only the second sum method is available because the type of the value produced by the lambda expression D => d.unitprice * D.unitcount is double. Therefore, the overload choice selects the second sum method to invoke.



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.