26.1.1 Type inference
When a generic method is called without specifying type arguments, a type inference process attempts to infer type arguments for the call. Lambda expressions passed as arguments to the generic method participant ipate in this type inference process.
When a generic function is called without a specific type of parameter, type inference attempts to deduce the parameter type for this call. Lambda expressions PASS Parameters to generic functions and participate in this type inference process.
As described in § 20. 6.4, type inference first occurs independently for each argument. in this initial phase, nothing is inferred from arguments that are lambda expressions. however, following the initial phase, additional inferences are made from lambda expressions using an iterative process. specifically, inferences are made as long as one or more arguments exist for which all of the following are true:
As described in section 6.4, type inference first occurs in each independent parameter. In the initialization phase, if the parameter is a lambda expression, nothing can be inferred. However, in the initialization phase, additional inferences will be made, which is done using an iterative process. In particular, as long as the following conditions are true, the inference of one or more parameters will be made:
· The argument is a lambda expression, in the following called L, from which no inferences have yet been made.
· The parameter is a lambda expression, called L, and has not been deduced.
· The corresponding parameter's type, in the following called P, is Del Egate type with a return type that involves one or more method type parameters.
· The corresponding parameter type, called P, is a proxy type and has a return value. The return value is associated with one or more function type parameters.
· P and L have the same number of parameters, and each parameter in P has the same modifiers as the corresponding parameter in L, or no modifiers if L has an implicitly typed parameter list.
· P and L have the same number of parameters, and each parameter in P has the same modifier, just like the corresponding L parameter, or can have no modifier, if L has an implicit parameter list.
· P's parameter types involve no method type parameters or involve only method type parameters for which a consistent set of inferences have already been made.
· The parameter type of P is not involved in the function type parameter or only involved in the function type parameter is for a set of consistent, has been inferred.
· If L has an explicitly typed parameter list, when inferred types are substituted for method type parameters in P, each parameter in P has the same type as the corresponding parameter in L.
· If L has an explicit type parameter list and the inferred type is used as a substitute for the function type parameter of P, the parameters in each P have the same, it is of the same type as the parameter in the corresponding L.
· If L has an implicitly typed parameter list, when inferred types are substituted for method type parameters in P and the resulting parameter types are given to the parameters of L, the body of L is a valid expression or statement block.
· If L has an implicit parameter list and the inferred type is used as a substitute for the function type parameter of P and the result parameter type has been given by the parameter of L, L The subject is a definite expression or declaration block.
· A return type can be inferred for L, as described below.
· Can be inferred for the return type of L, as described above.
For each such argument, inferences are made from that argument by relating the return type of P with the inferred return type of L and the new inferences are added to the accumulated set of inferences. this process is repeated until no further inferences can be made.
For each such parameter, the inference is made from the parameter. This process is implemented through the return type of the correlated P and the return type of the inferred L, new inferences are added to a set of inferences that have been accumulated. This process repeats until no inference can be made.
For purposes of type inference and overload resolution, the inferred return type of a lambda expression L is determined as follows:
The purpose of type inference and overload decision is to determine the types of a lambda expression L as follows:
· If the body of L is an expression, the type of that expression is the inferred return type of L.
· If the subject of L is an expression, the expression type is inferred from the type of L's return.
· If the body of L is a statement block, if the set formed by the types of the expressions in the block's return statements contains exactly one type to which each type in the set is implicitly convertible, and if that type is not the null type, then that type is the inferred return type of L.
· If the body of L is a declaration block, and this set of return statements formatted by expression type in the Declaration block contains a type that is indeed true, in addition, each type in this set can be implicitly converted, and that type is not null, the type can be inferred from the return type of L.
· Otherwise, a return type cannot be inferred for L.
· On the contrary, the return type cannot be inferred by L.
As an example of type inference involving lambda expressions, consider the select Extension Method declared in the system. query. Sequence class:
As an example of type inference that contains lambda expressions, consider the select Extension function, which is 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 );
}
}
}
Assuming the System. query namespace was imported with a using clause, and given a class Customer with a Name property of type string, the Select method can be used to select the names of a list of MERs:
Assume that System. the Query namespace is imported using the using statement and a class named Customer is provided. This class has a Name attribute of the string type, the Select function can be used to Select a group of Customer names:
List <Customer> MERs = GetCustomerList ();
IEnumerable <string> names = MERs. Select (c => c. Name );
The extension method invocation (§ 26. 2.3) of Select is processed by rewriting the invocation to a static method invocation:
The extended function call of the Select statement (§ 26. 2.3) is done by rewriting the static function:
IEnumerable <string> names = Sequence. Select (MERS MERs, c => c. Name );
Since type arguments were not explicitly specified, type inference is used to infer the type arguments. first, the customers argument is related to the source parameter, inferring T to be Customer. then, using the lambda expression type inference process described above, c is given type Customer, and the expression c. name is related to the return type of the selector parameter, inferring S to be string. thus, the invocation is equivalent
Since the type parameter is not explicitly specified, only the type inference is used to derive the type parameter. First, the customers parameter is associated with the source parameter, and it is inferred that T is Customer. Then, the lambda expression type inference is used for processing. As described above, c is given to the Customer type, and the expression c. Name is associated with the return type parameter of a select statement. The call is equal
Sequence. Select <Customer, string> (MERS MERs, (Customer c) => c. Name)
And the result is of type IEnumerable <string>.
And the result type is IEnumerable <string>.
The following example demonstrates how lambda expression type inference allows type information to "flow" between arguments in a generic method invocation. Given the method
The following example shows how to use lambda expression type inference so that the type information can be "followed" in the parameter, which occurs in the call of a generic function. The provided function is
Static z f <X, Y, Z> (X value, Func <X, Y> f1, Func <Y, Z> f2 ){
Return f2 (f1 (value ));
}
Type inference for the invocation
Type inference for this call
Double seconds = F ("1:15:30", s => TimeSpan. Parse (s), t => t. TotalSeconds );
Proceeds as follows: First, the argument "1:15:30" is related to the value parameter, inferring X to be string. then, the parameter of the first lambda expression, s, is given the inferred type string, and the expression TimeSpan. parse (s) is related to the return type of f1, inferring Y to be System. timeSpan. finally, the parameter of the second lambda expression, t, is given the inferred type System. timeSpan, and the expression t. totalSeconds is related to the return type of f2, inferring Z to be double. thus, the result of the invocation is of type double.
The 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 is of the string type, and the TimeSpan. Parse (s) expression is associated with the return type of f1, and Y is of the System. TimeSpan type. Finally, the parameter t of the second lambda expression is given to the System. TimeSpan type, and the t. TotalSeconds expression is associated with the return type of f2, And the Z is double. The result type of this call is double.