Version 3.0 Specification
September 2005
Notice
© 2005 Microsoft Corporation. All rights
reserved.
Microsoft, Windows, Visual Basic, Visual C#, and
Visual C++ are either registered trademarks or
trademarks of Microsoft Corporation in the U.S.A.
and/or other countries/regions.
Other product and company names mentioned herein
may be the trademarks of their respective owners.
Table of Contents
目录
26.
Overview of C# 3.0
(C#
3.0
概述)............................................................................................
5
26.1
Implicitly typed local variables
隐型局部变量(Implicity
typed local
variables)......................................
5
26.2
Extension methods
扩展方法............................................................................................................
6
26.2.1
Declaring extension methods
声明扩展方法................................................................................
6
26.2.2
Importing extension methods
导入扩展方法................................................................................
7
26.2.3
Extension method invocations
扩展方法的调用...........................................................................
7
26.3 Lambda
expressions Lambda表达式.................................................................................................
8
26.3.1
Lambda expression conversions Lambda
表达式转换................................................................
10
26.3.2 Type
inference 类型推导.........................................................................................................
11
26.3.3
Overload resolution 重载决议...................................................................................................
13
26.4 Object
and collection initializers 对象和集合的初始化器.................................................................
14
26.4.1
Object initializers 对象初始化器...............................................................................................
14
26.4.2
Collection initializers 集合初始化器..........................................................................................
16
26.5
Anonymous types
匿名类型............................................................................................................
17
26.6
Implicitly typed arrays 隐型数组(Implicitly
typed
arrays)..................................................................
18
26.7 Query
expressions
查询表达式.......................................................................................................
19
26.7.1
Query expression translation 查询表达式的转换.......................................................................
20
26.7.1.1
where clauses where子句...................................................................................................
21
26.7.1.2
select clauses select
子句...................................................................................................
21
26.7.1.3
group clauses group子句.....................................................................................................
21
26.7.1.4
orderby clauses orderby
子句..............................................................................................
22
26.7.1.5
Multiple generators
多重产生器(generator)
.......................................................................
22
26.7.1.6
into clauses info
子句..........................................................................................................
23
26.7.2 The
query expression pattern
查询表达式模式..........................................................................
23
26.7.3
Formal translation rules
正式的转换规则..................................................................................
24
26.8
Expression trees
表达式树............................................................................................................
26
26. Overview of C# 3.0
(C# 3.0概述)
C# 3.0 (“C#
Orcas”) introduces several language
extensions that build on C# 2.0 to support
the creation and use of higher order,
functional style class libraries. The
extensions enable construction of
compositional APIs that have equal
expressive power of query languages in
domains such as relational databases and
XML. The extensions include:
C# 3.0 (“C#
魔兽(Orcas)”)
引入了几个构建在C#
2.0上的语言扩展,用来支持创建和使用更高级的函数式(functional或译:泛函)类库。这些扩展允许组合(compositional)APIs的构造,这些APIs与关系数据库和XML等领域中的查询语言具有同等的表达力。
·
Implicitly typed local variables, which
permit the type of local variables to be
inferred from the expressions used to
initialize them.
·
隐型局部变量,允许局部变量的类型从初始化它们的表达式推导而来。
·
Extension methods, which make it possible to
extend existing types and constructed types
with additional methods.
·
扩展方法,使得使用附加(additional)的方法扩展已存在的类型和构造类型成为可能。
·
Lambda expressions, an evolution of
anonymous methods that provides improved
type inference and conversions to both
delegate types and expression trees.
·
Lambda
表达式,是匿名方法的演进,可提供改良的类型推导和到dalegate类型和表达式树的转换。
·
Object initializers, which ease construction
and initialization of objects.
·
对象初始化器,简化了对象的构造和初始化。
·
Anonymous types, which are tuple types
automatically inferred and created from
object initializers.
·
匿名类型,是从对象初始化器自动推导和创建的元组(tuple)类型。
·
Implicitly typed arrays, a form of array
creation and initialization that infers the
element type of the array from an array
initializer.
·
隐型数组,数组创建和初始化的形式,它从数组初始化器推导出数组的元素类型。
·
Query expressions, which provide a language
integrated syntax for queries that is
similar to relational and hierarchical query
languages such as SQL and XQuery.
·
查询表达式,为类似于关系型和层次化查询语言(比如SQL和XQuery)提供一个语言集成(intergrated)的语法。
·
Expression trees, which permit lambda
expressions to be represented as data
(expression trees) instead of as code
(delegates).
·
表达式树,允许lambda表达式表示为数据(表达式树)而不是代码(delegate)。
This
document is a technical overview of those features.
The document makes reference to the C# Language Specification
1.2 (§1 through §18) and the C# Language Specification
2.0 (§19 through §25), both of which are available on
the C# Language Home Page (http://msdn.microsoft.com/vcsharp/language).
本文档是这些特征的技术概述。文档引用了C#语言规范1.2(§1-§18)和C#语言规范2.0(§19-§25),这两个规范都在C#语言主页上(http://msdn.microsoft.com/vcsharp/language)。
26.1
Implicitly typed local variables隐型局部变量
In an
implicitly typed local variable
declaration, the type of the
local variable being declared is inferred
from the expression used to initialize the
variable. When a local variable declaration
specifies
var
as the type and no type named
var
is in scope, the declaration is an
implicitly typed local variable declaration.
For example:
在隐型局部变量声明中,正被声明的局部变量的类型从初始化这个变量的表达式推导得来。当局部变量声明指明var作为类型,并且该范围域(scope)中没有var名称的类型存在,这个声明就称为隐型局部声明。例如:
var i = 5;
var s = "Hello";
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();
The implicitly
typed local variable declarations above are
precisely equivalent to the following
explicitly typed declarations:
上面的隐型局部变量声明精确地等同于下面的显型(explicitly
typed)声明:
int i = 5;
string s = "Hello";
double d = 1.0;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new
Dictionary<int,Order>();
A local
variable declarator in an implicitly typed
local variable declaration is subject to the
following restrictions:
隐型局部变量声明中的局部变量声明符(declarator)遵从下面这些约束:
·
The declarator must include an initializer.
·
声明符必须包含初始化器。
·
The initializer must be an expression. The
initializer cannot be an object or
collection initializer (§26.4) by itself,
but it can be a
new
expression that includes an object or
collection initializer.
·
初始化器必须是一个表达式。初始化器不能是一个自身的对象或者集合初始化器(26.4),但是它可以是包含一个对象或集合初始化器的一个new表达式。
·
The compile-time type of the initializer
expression cannot be the null type.
·
初始化器表达式的编译期类型不可以是空(null)类型。
·
If the local variable declaration includes
multiple declarators, the initializers must
all have the same compile-time type.
·
如果局部变量声明包含了多个声明符,这些声明符必须具备同样的编译期类型。
The following
are examples of incorrect implicitly typed
local variable declarations:
下面是一些不正确的隐型局部变量声明的例子:
var
x; // Error, no initializer to
infer type from
var y = {1, 2, 3}; // Error, collection
initializer not permitted
var z = null; // Error, null type not
permitted
For reasons of
backward compatibility, when a local
variable declaration specifies
var
as the type and a type named
var
is in scope, the declaration refers to that
type; however, a warning is generated to
call attention to the ambiguity. Since a
type named
var
violates the established convention of
starting type names with an upper case
letter, this situation is unlikely to occur.
因为向后兼容的原因,当局部变量声明指定var作为类型,而范围域中又存在叫var的类型,则这个声明会推导为那个叫var的类型;然后,会产生一个关注含糊性(ambiguity)的警告,因为叫var的类型违反了既定的类名首字母大写的约定,这个情形也未必会出现。(译者:视编译器实现而定)
The
for-initializer of a
for
statement (§8.8.3) and the
resource-acquisition of a
using
statement (§8.13) can be an implicitly typed
local variable declaration. Likewise, the
iteration variable of a
foreach
statement (§8.8.4) may be declared as an
implicitly typed local variable, in which
case the type of the iteration variable is
inferred to be the element type of the
collection being enumerated. In the example
for表达式(§8.8.3)的for初始化器(for-initializer)
和using表达式的资源获取(resource-acquisition)可以作为一个隐型局部变量声明。同样,foreach表达式(§8.8.4)的迭代变量可以声明为一个隐型局部变量,这种情况下,(隐型局部变量的)类型推导为正被枚举(enumerated)的集合的元素的类型。例子:
int[] numbers =
{ 1, 3, 5, 7, 9 };
foreach (var n in numbers)
Console.WriteLine(n);
the type of
n
is inferred to be
int,
the element type of
numbers.
n的类型推导为numbers的元素类型int。
26.2
Extension methods扩展方法
Extension methods are static
methods that can be invoked using instance
method syntax. In effect, extension methods
make it possible to extend existing types
and constructed types with additional
methods.
扩展方法是可以通过使用实例方法语法调用的静态方法。效果上,扩展方法使得用附加的方法扩展已存在类型和构造类型成为可能。
Note
Extension methods are less discoverable and more limited
in functionality than instance methods. For those
reasons, it is recommended that extension methods
be used sparingly and only in situations where instance
methods are not feasible or possible.
Extension members of other kinds,
such as properties, events, and operators, are being
considered but are currently not supported.
注意
扩展方法不容易被发现并且在功能上比实例方法更受限。由于这些原因,推荐保守地使用和仅在实例方法不可行或不可能的情况下使用。
其它种类的扩展方法,比如属性、事件和操作符,正在被考虑当中,但是当前并不被支持。
26.2.1
Declaring extension methods声明扩展方法
Extension
methods are declared by specifying the
keyword
this
as a modifier on the first parameter of the
methods. Extension methods can only be
declared in static classes. The following is
an example of a static class that declares
two extension methods:
扩展方法是通过指定关键字this修饰方法的第一个参数而声明的。扩展方法仅可声明在静态类中。下面是声明了两个扩展方法的静态类的例子:
namespace
Acme.Utilities
{
public static class
Extensions
{
public static int
ToInt32(this string s) {
return Int32.Parse(s);
}
public
static T[] Slice<T>(this
T[] source, int index, int count) {
if (index < 0 || count < 0 ||
source.Length – index < count)
throw new ArgumentException();
T[] result = new T[count];
Array.Copy(source, index, result, 0,
count);
return result;
}
}
}
Extension
methods have all the capabilities of regular
static methods. In addition, once imported,
extension methods can be invoked using
instance method syntax.
扩展方法具备所有常规静态方法的所有能力。另外,一旦被导入,扩展方法可以使用实例方法语法调用之。
26.2.2 Importing
extension methods导入扩展方法
Extension
methods are imported through
using-namespace-directives (§9.3.2). In
addition to importing the types contained in
a namespace, a using-namespace-directive
imports all extension methods in all static
classes in the namespace. In effect,
imported extension methods appear as
additional methods on the types that are
given by their first parameter and have
lower precedence than regular instance
methods. For example, when the
Acme.Utilities
namespace from the example above is imported
with the using-namespace-directive
扩展方法用using-namespace-directives
(§9.3.2)导入。除了导入包含在名字空间中的类型外,using-namespace-directives
也导入了名字空间中所有静态类中的所有扩展方法。实际上,被导入的扩展方法作为被修饰的第一个参数类型上的附加方法出现,并且相比常规实例方法具有较低的优先权。比如,当使用using-namespace-directive导入上个例子中Acme.Utilities
名字空间:
using
Acme.Utilities;
it becomes
possible to invoke the extension methods in
the static class
Extensions
using instance method syntax:
它使得可以在静态类Extension上使用实例方法语法调用扩展方法:
string s =
"1234";
int i = s.ToInt32(); // Same
as Extensions.ToInt32(s)
int[] digits =
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] a = digits.Slice(4, 3); // Same
as Extensions.Slice(digits, 4, 3)
26.2.3 Extension
method invocations扩展方法调用
The detailed
rules for extension method invocation are
described in the following. In a method
invocation (§7.5.5.1) of one of the forms
扩展方法调用的详细规则表述如下。以如下调用形式之一:
expr
.
identifier
( )
expr
.
identifier
(
args
)
expr
.
identifier
<
typeargs
> ( )
expr
.
identifier
<
typeargs
> (
args
)
if the normal
processing of the invocation finds no
applicable instance methods (specifically,
if the set of candidate methods for the
invocation is empty), an attempt is made to
process the construct as an extension method
invocation. The method invocation is first
rewritten to one of the following,
respectively:
如果调用的正常处理过程发现没有适用的实例方法(特别地,如果这个调用的候选方法集是空的),就会试图处理扩展方法调用的构造。方法调用会首先被分别重写称如下之一:
identifier
(
expr
)
identifier
(
expr
,
args
)
identifier
<
typeargs
> (
expr
)
identifier
<
typeargs
> (
expr
,
args
)
The rewritten
form is then processed as a static method
invocation, except for the way in which
identifier is resolved: Starting with
the closest enclosing namespace declaration,
continuing with each enclosing namespace
declaration, and ending with the containing
compilation unit, successive attempts are
made to process the rewritten method
invocation with a method group consisting of
all accessible extension methods with the
name given by identifier imported
by the namespace declaration’s
using-namespace-directives. The first
method group that yields a non-empty set of
candidate methods is the one chosen for the
rewritten method invocation. If all attempts
yield empty sets of candidate methods, a
compile-time error occurs.
重写后的形式然后被作为静态方法调用处理,除非标识符identifier决议为:以最靠近的封闭名字空间声明开始,以每个封闭名字空间声明继续,并以包含的编译单元结束,持续地试图用组成所有可访问的,由using-namespace-directives导入的,指明为
identifier名字的扩展方法处理重写的方法调用。第一个产生非空候选方法集的方法组(method
group)就成为被选中的重写的方法调用。如果所有的努力都只产生空的候选集,则发生编译期错误。
The preceeding
rules mean that instance methods take
precedence over extension methods, and
extension methods imported in inner
namespace declarations take precedence over
extension methods imported in outer
namespace declarations. For example:
前面的规则标表明实例方法优先于扩展方法,并且导入进内层名字空间中的扩展方法优先于导入进外层名字空间中的扩展方法。例如:
using N1;
namespace N1
{
public static class E
{
public static void F(this object obj,
int i) { }
public
static void F(this object obj, string s) { }
}
}
class A { }
class B
{
public void F(int i) { }
}
class C
{
public void F(object obj) { }
}
class X
{
static void Test(A a, B b, C c) {
a.F(1); // E.F(object, int)
a.F("hello"); // E.F(object,
string)
b.F(1); // B.F(int)
b.F("hello"); // E.F(object,
string)
c.F(1); // C.F(object)
c.F("hello"); // C.F(object)
}
}
In the example,
B’s
method takes precedence over the first
extension method, and
C’s
method takes precedence over both extension
methods.
例子中,B的方法优先于第一个扩展方法,C的方法优先于两个扩展方法。
26.3 Lambda
expressions
Lambda表达式
C# 2.0
introduces anonymous methods, which allow
code blocks to be written “in-line” where
delegate values are expected. While
anonymous methods provide much of the
expressive power of functional programming
languages, the anonymous method syntax is
rather verbose and imperative in nature.
Lambda expressions
provide a more concise, functional syntax
for writing anonymous methods.
C# 2.0
引入了匿名方法,它允许在delegate值(delegate
value) (译者:delegate对象)被需要的地方以内联(in-line)方式写一个代码块。当匿名方法提供了大量函数式编程语言(或泛函编程)(functional
programming)的表达力时,实质上,匿名方法的语法是相当烦琐和带有强制性的。Lambda表达式提供了一个更加简练的函数式语法来写匿名方法。
A lambda
expression is written as a parameter list,
followed by the
=>
token, followed by an expression or a
statement block.
Lambda表达式写成一个后面紧跟
=>
标记的参数列表,=>之后是一个表达式或表语句块。
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 a lambda expression can be explicitly or
implicitly typed. In an explicitly typed
parameter list, the type of each parameter
is explicitly stated. In an implicitly typed
parameter list, the types of the parameters
are inferred from the context in which the
lambda expression occurs—specifically, when
the lambda expression is converted to a
compatible delegate type, that delegate type
provides the parameter types (§26.3.1).
Lambda表达式的参数可以是显型和隐型的。在显型参数列表中,每个参数的类型是显式指定的。在隐型参数列表中,参数的类型由lambda表达式出现的语境推导——特定地,当lambda表达式被转型到一个兼容的delegate类型时,delegate类型提供参数的类型(§26.3.1)。
In a lambda
expression with a single, implicitly typed
parameter, the parentheses may be omitted
from the parameter list. In other words, a
lambda expression of the form
在有单一的隐型参数的lambda表达式中,圆括号可以从参数列表中省略。换句话说,如下形式的lambda表达式
(
param
) =>
expr
can be
abbreviated to
可以被简写成
param
=>
expr
Some examples
of lambda expressions follow below:
下面是一些lambda表达式的例子:
x => x +
1 // Implicitly typed,
expression body
x => { return x
+ 1; } // Implicitly typed, statement
body
(int x) => x +
1 // Explicitly typed,
expression body
(int x) => {
return x + 1; } // Explicitly typed,
statement body
(x, y) => x *
y // Multiple parameters
() =>
Console.WriteLine() // No parameters
In general, the
specification of anonymous methods, provided
in §21 of the C# 2.0 Specification, also
applies to lambda expressions. Lambda
expressions are a functional superset of
anonymous methods, providing the following
additional functionality:
通常,C#
2.0规范§21中提供的匿名方法规范,也应用上了lambda表达式。Lambda表达式是匿名方法的泛函超集,它提供了如下附加功能:
·
Lambda expressions permit parameter types to
be omitted and inferred whereas anonymous
methods require parameter types to be
explicitly stated.
·
Lambda表达式允许参数类型被省略掉和被推导,尽管匿名方法要求显式指定参数类型。
·
The body of a lambda expression can be an
expression or a statement block whereas the
body of an anonymous method can only be a
statement block.
·
Lambda表达式体可以是一个表达式或者语句块,尽管匿名方法体可以是一个语句块。
·
Lambda expressions passed as arguments
participate in type argument inference
(§26.3.2) and in method overload resolution
(§26.3.3).
·
Lambda表达式作为参数传递参与类型参数推导(§26.3.2)和重载决议。
·
Lambda expressions with an expression body
can be converted to expression trees
(§26.8).
·
带有表达式体的Lambda表达式可以被转换成表达式树(§26.8)。
Note
The
PDC 2005 Technology Preview compiler does not support
lambda expressions with a statement block body. In
cases where a statement block body is needed, the
C# 2.0 anonymous method syntax must be used.
注意
PDC
2005技术预览编译器不支持带有语句体的lambda表达式。在需要语句体的情况下,必须使用C#
2.0匿名方法语法。
26.3.1 Lambda
expression conversions
Lambda表达式转换
Similar to an
anonymous-method-expression, a
lambda-expression is classified as a
value with special conversion rules. The
value does not have a type but can be
implicitly converted to a compatible
delegate type. Specifically, a delegate type
D
is compatible with a lambda-expression
L
provided:
与匿名方法表达式(anonymous-method-expression)类似,lambda表达式是用特殊转换规则作为值(value)类型分类的。这个值(value)没有类型,但是可以隐式转型至一个兼容的delegate类型。特别地,delegate类型D与lambda表达式L兼容的,如果:
·
D
and
L
have the same number of parameters.
·
D和L有相同数目的参数。
·
If
L
has an explicitly typed parameter list, each
parameter in
D
has the same type and modifiers as the
corresponding parameter in
L.
·
如果L有显型参数列表,D中的每个参数有着与相应的L中的参数相同的类型和修饰符。
·
If
L
has an implicitly typed parameter list,
D
has no
ref
or
out
parameters.
·
如果L有隐型参数列表,D不可有ref或out参数。
·
If
D
has a
void
return type and the body of
L
is an expression, when each parameter of
L
is given the type of the corresponding
parameter in
D,
the body of
L
is a valid expression that would be
permitted as a statement-expression
(§8.6).
·
如果D有void
返回类型,并且L的体(body)是一个表达式,当L的每个参数被给定为对应的D中参数的类型时,L的体是一个允许作为语句-表达式(statement-expression(§8.6))的有效表达式
·
If
D
has a
void
return type and the body of
L
is a statement block, when each parameter of
L
is given the type of the corresponding
parameter in
D,
the body of
L
is a valid statement block in which no
return
statement specifies an expression.
·
如果D有void返回类型并且L的体是语句块,当L的每个参数类型是被给定为相应的D参数的类型时,L的体是一个没有返回语句的有效语句块。
·
If
D
has a non-void return type and the body of
L
is an expression, when each parameter of
L
is given the type of the corresponding
parameter in
D,
the body of
L
is a valid expression that is implicitly
convertible to the return type of
D.
·
如果D有non-void返回值并且L的体是一个表达式,当L的每个参数类型是被给定的相应于D参数的类型时,L的体是一个可以隐式转换到D返回类型的有效表达式。
·
If
D
has a non-void return type and the body of
L
is a statement block, when each parameter of
L
is given the type of the corresponding
parameter in
D,
the body of
L
is a valid statement block with a
non-reachable end point in which each
return
statement specifies an expression that is
implicitly convertible to the return type of
D.
·
如果D有non-void返回值并且L的体是一个语句块,当L的每个参数类型是被给定的相应于D参数的类型时,L的体是一个有效的语句块,语句块中有不可到达(non-reachable)的终点(end
point)(译者:是否应该为“没有不可到达的终点”),且每个终点的返回语句指明一个可以隐式转换到D返回类型的表达式。
The examples
that follow use a generic delegate type
Func<A,R>
which represents a function taking an
argument of type
A
and returning a value of type
R:
下面的例子使用泛型delegagte类型Func<A,R>表示一个带有参数类型A和返回类型R的函数:
delegate R
Func<A,R>(A arg);
In the
assignments
赋值如下:
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 parameter
and return types of each lambda expression
are determined from the type of the variable
to which the lambda expression is assigned.
The first assignment successfully converts
the lambda expression to the delegate type
Func<int,int>
because, when
x
is given type
int,
x+1
is a valid expression that is implicitly
convertible to type
int.
Likewise, the second assignment successfully
converts the lambda expression to the
delegate type
Func<int,double>
because the result of
x+1
(of type
int)
is implicitly convertible to type
double.
However, the third assignment is a
compile-time error because, when
x
is given type
double,
the result of
x+1
(of type
double)
is not implicitly convertible to type
int.
每个Lambda表达式的参数和返回类型决定于lambda表达式被赋值的变量的类型。第一个赋值成功地转换lambda表达式到delegate类型Func<int,int>,是因为当
x是int型,x+1
是一个有效的表达式并可以隐式地转换到类型int。同样第二个赋值成功地转换lambda表达式到delegate类型Func<int,double>,是因为x+1的返回值(类型int)是隐式转换成double的。然而第三个赋值有编译期错误,因为当x是double,x+1是double,不能够隐式转变到类型int。
26.3.2 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 participate in this type
inference process.
当泛型方法被调用而不指明类型参数时,参数推导过程试图从调用中推导出类型参数。Lambda表达式参数传递给泛型方法参与这个类型推导过程。
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:
如同§20.6.4中表述的那样,类型推导首先为每个参数独立的发生。在初始阶段,不能从lambda表达式参数推导出任何东西。然而,初始阶段之后,产生了使用迭代过程的额外的推导。特别地,只要有一个或多个满足如下条件为真的参数存在,推导将会产生:
·
The argument is a lambda expression, in the
following called
L,
from which no inferences have yet been made.
·
参数是lambda表达式,下面称为L,从中,尚无推导。
·
The corresponding parameter’s type, in the
following called
P,
is a delegate type with a return type that
involves one or more method type parameters.
·
相应的参数类型,下面称为P,是有返回类型的含有一个或多个方法类型参数的delegate。
·
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和L拥有相同数目的参数,并且P中的每个参数与L中相应的参数具有相同的修饰符,或者如果L有隐型参数列表时,没有修饰符。
·
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.
·
P的参数类型不包含方法类型参数或者包含仅仅一个方法类型参数,对这个参数已经产生一个相容的推导集。
·
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 the corresponding
parameter in
L.
·
如果L有一个显型参数列表,当推导出的类型对于P中的方法类型参数是可替换的时候,P中的每个参数拥有与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.
·
如果L有一个隐型参数列表,当推导出的类型对于P中的方法类型参数是可替代的,并且返回参数类型被给予L的参数,L的体是一个有效表达式或语句块。
·
A
return type can be inferred for
L,
as described below.
·
返回类型可以为L推导出来,描述如下:
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.
对每一个这样的参数,将会通过关联P的返回类型和L的推导返回类型做出推论,并且新的推论被加入进累积的推论集。这个过程将重复进行,直到没有更进一步的推论产生为止。
For purposes of
type inference and overload resolution, the
inferred return type
of a lambda expression
L
is determined as follows:
因为类型推导和重载决议的原因,lambda表达式L推导出的类型决定于下面:
·
If the body of
L
is an expression, the type of that
expression is the inferred return type of
L.
·
如果L的体是一个表达式,表达式的类型就是推导出的L的返回类型。
·
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.
·
如果L的体是一个语句块,如果由语句块中return语句表达式的类型形成的集合(set)正好包含一个集合中每个类型都可隐式转换成的类型,那么这个类型就是推导出的L的返回类型。(译者:如果有个集合{int,
byte, double},则double满足要求)
·
Otherwise, a return type cannot be inferred
for
L.
·
此外,返回类型不能为L推导出来。
As an example
of type inference involving lambda
expressions, consider the
Select
extension method declared in the
System.Query.Sequence
class:
作为一个包含lambda表达式的类型推导的例子,考虑声明于System.Query.Sequence类中的Select扩展方法:
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 customers:
假定System.Query名字空间使用using子句导入,并且给出一个类Customer,带有类型为string的属性Name,
Select方法可用作选择一列(list
of )customers的名字
List<Customer>
customers = GetCustomerList();
IEnumerable<string> names =
customers.Select(c => c.Name);
The extension
method invocation (§26.2.3) of
Select
is processed by rewriting the invocation to
a static method invocation:
Select扩展方法调用通过重写静态方法调用处理:
IEnumerable<string> names =
Sequence.Select(customers, 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 to
因为类型参数未被显式指明,将会使用类型推导来推导类型参数。首先customers参数被关联到source参数,推导T是Customer。然后使用前面描述的lambda表达式类型推导过程,
c是给定类型Customer,而表达式c.Name被关联到selector参数的返回类型上,推导s是string,这样,调用就等价于
Sequence.Select<Customer,string>(customers,
(Customer c) => c.Name)
and the result
is of type
IEnumerable<string>.
返回类型是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
下面的例子示范了lambda表达式类型推导是如何允许类型信息在泛型函数调用的参数之间“流动”的。给出方法
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
调用的类型推导
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.
处理过程如下:首先参数”1:15:30”被关联到值参数,推导X是string。然后第一个lambda表达式的参数s是给定推导类型string,并且表达式TimeSpan.Parse(s)被关联到f1的返回类型上,推导Y为System.TimeSpan。最后第二个lambda表达式的参数t,是给定为推导类型System.TimeSpan,表达式t.ToTalSeconds被关联到f2的返回类型上,推导Z为double。这样,调用的返回类型就是doub