C# Version 3.0 Specification

来源:互联网 发布:解压破解密码软件 编辑:程序博客网 时间:2024/05/23 18:20
September 2005
翻译: 邱龙斌 <qiu_lb (at) hotmail.com>
2005-09-15
得益于互联网的开放性和专业人员的共享精神,过去几年里我在网络上搜索到很多重要的参考资料和电子文档。在此对大家的奉献性的工作表示感谢。
近日无意中发现了Microsoft的LINQ项目,这个项目是用来试验C#未来版本也就是3.0版本的新功能的。有兴趣的朋友可以到LINQ项目主页去看看,上面有C# 3.0 和LINQ的介绍、示例代码。http://msdn.microsoft.com/netframework/future/linq/
本人使用c++多年,深知语言核心的稳定和程序库的激进同样重要。对c++而言boost提供了许多库扩展方面的最佳实践,比如boost.python,boost.function,boost.lambda等。新的c++0x标准提案中提到在语言核心层直接支持concept和model的概念,从而在编译期进行concept和model的检查,就类型约束这点,我知道c#2.0泛型是用where表示泛型类型参数的约束的。
C#语言核心,近年来动作很大,继2.0加入泛型、匿名方法、迭代器、不完整类型、Nullable类型之后,3.0更是加入了一些引人注目的新特性。感慨之余,开发人员又要继续学习了;同时开始担心例如Mono,DotGnu等开源.Net项目。
浏览了一下C# 3.0 Specification,感觉C#有越来越动态化的倾向,数据查询方面也更直接。花了点时间翻译成中文,希望对有需要的朋友有用。翻译错误再所难免,有问题的朋友可以跟我联系,讨论本文的翻译问题。
声明:本译文不可用于商业目的流传, Microsoft可能有异议。
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.
Copyright 2 Microsoft Corporation 2005. All Rights Reserved.
Overview of C# 3.0
目录
26.C# 3.0概述..............................................................................................................................................3
26.1隐型局部变量(implicitly typed local variable)..................................................................................3
26.2扩展方法................................................................................................................................................4
26.2.1声明扩展方法.....................................................................................................................................4
26.2.2导入扩展方法.....................................................................................................................................4
26.2.3扩展方法调用.....................................................................................................................................5
26.3Lambda表达式.....................................................................................................................................6
26.3.1Lambda表达式转换..........................................................................................................................7
26.3.2类型推导.............................................................................................................................................8
26.3.3Overload resolution 重载决议.......................................................................................................10
26.4对象和集合初始化器...........................................................................................................................10
26.4.1Object initializers 对象初始化器....................................................................................................11
26.4.2集合初始化器....................................................................................................................................13
26.5匿名类型...............................................................................................................................................14
26.6隐型数组(Implicitly typed arrarys)...............................................................................................15
26.7查询表达式...........................................................................................................................................16
26.7.1查询表达式translation....................................................................................................................17
26.7.1.1where子句....................................................................................................................................17
26.7.1.2select子句.....................................................................................................................................17
26.7.1.3group子句.....................................................................................................................................18
26.7.1.4orderby子句..................................................................................................................................18
26.7.1.5多重产生器..................................................................................................................................18
26.7.1.6info子句........................................................................................................................................19
26.7.2查询表达式模式..............................................................................................................................19
26.7.3正式的转换规则..............................................................................................................................20
26.8表达式树(Expression trees).......................................................................................................22

【26.C# 3.0概述】
C# 3.0 (“C# 魔兽(Orcas)”) 引入了几个构建在C# 2.0上的语言扩展,用来支持创建和使用更高级的函数式(functional或译:泛函)类库。这些扩展允许 组合 (compositional)APIs 的 构造,这些APIs与关系数据库和XML等领域中的查询语言具有同等的表达力。
隐型局部变量 ,允许局部变量的类型从初始化它们的表达式推导而来。
扩展方法 ,使得使用附加(additional)的方法扩展已存在的类型和构造类型成为可能。
Lambda 表达式 ,是匿名方法的演进,可提供改良的类型推导和到 dalegate 类 型和表达式树的 转换。
对象初始化器 ,简化了对象的构造和初始化。
匿名类型 ,是从对象初始化器自动推导和创建的元组(tuple)类型。
隐型数组 ,数组创建和初始化的形式,它从数组初始化器推导出数组的元素类型。
查询表达式 ,为 类似于关系型和层次化查询语言(比如 S QL 和 X Query ) 提供一个语言集成(intergrated)的语法。
表达式树 ,允许lambda表达式表示为数据(表达式树)而不是代码(delegate)。
本文档是这些特征的技术概述。文档引用了C#语言规范1.2(§1-§18)和C#语言规范2.0(§19-§25),这两个规范都在C#语言主页上(http://msdn.microsoft.com/vcsharp/language)。
【26.1 隐型局部变量(implicitly typed local variable)】
在隐型局部变量声明中,正被声明的局部变量的类型从初始化这个变量的表达式推导得来。当局部变量声明指明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>();
上面的隐型局部变量声明精确地等同于下面的显型(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>();
隐型局部变量声明中的局部变量声明符(declarator)遵从下面这些约束:
 声明符必须包含初始化器。
 初始化器必须是一个表达式。初始化器不能是一个自身的对象或者集合初始化器(§)),但是它可以是包含一个对象或集合初始化器的一个new表达式。
 初始化器表达式的编译期类型不可以是空(null)类型。
 如果局部变量声明包含了多个声明符,这些声明符必须具备同样的编译期类型。
下面是一些不正确的隐型局部变量声明的例子:
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
因为向后兼容的原因,当局部变量声明指定var作为类型,而范围域中又存在叫var的类型,则这个声明会推导为那个叫var的类型;然后,会产生一个关注含糊性(ambiguity)的警告,因为叫var的类型违反了既定的类名首字母大写的约定,这个情形也未必会出现。(译者:视编译器实现而定)
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);
n的类型推导为numbers的元素类型int。
【26.2 扩展方法】
扩展方法 是可以通过使用实例方法语法调用的静态方法。效果上,扩展方法使得用附加的方法扩展已存在类型和构造类型成为可能。
注意:
扩展方法不容易被发现并且在功能上比实例方法更受限。由于这些原因,推荐保守地使用和仅在实例方法不可行
或不可能的情况下使用。其它种类的扩展方法,比如属性、事件和操作符,正在被考虑当中,但是当前并不被支持。
【26.2.1 声明扩展方法】
扩展方法是通过 指定关键字 t his 修 饰方法的第一个参数 而声明的。扩展方法仅可声明在静态类中。下面是声明了两个扩展方法的静态类的例子:
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;
}
}
}
扩展方法具备所有常规静态方法的所有能力。另外,一旦被导入,扩展方法可以使用实例方法语法调用之。
【26.2.2 导入扩展方法】
扩展方法用using-namespace-directives (§9.3.2)导入。除了导入包含在名字空间中的类型外,usingnamespace-directives 也导入了名字空间中所有静态类中的所有扩展方法。实际上,被导入的扩展方法作
4 Copyright 2 Microsoft Corporation 2005. All Rights Reserved.
为被修饰的第一个参数类型上的附加方法出现,并且相比常规实例方法具有较低的优先权。比如,当使用using-namespace-directive导入上个例子中Acme.Utilities 名字空间:
using Acme.Utilities;
它使得可以在静态类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 扩展方法调用】
扩展方法调用的详细规则表述如下。以如下调用形式之一:
expr . identifier ( )
expr . identifier ( args )
expr . identifier < typeargs > ( )
expr . identifier < typeargs > ( args )
如果调用的正常处理过程发现没有适用的实例方法(特别地,如果这个调用的候选方法集是空的),就会试图处理扩展方法调用的构造。方法调用会首先被分别重写称如下之一:
identifier ( expr )
identifier ( expr , args )
identifier < typeargs > ( expr )
identifier < typeargs > ( expr , args )
重写后的形式然后被作为静态方法调用处理,除非标识符identifier决议为:以最靠近的封闭名字空间声明开始,以每个封闭名字空间声明继续,并以包含的编译单元结束,持续地试图用组成所有可访问的,由 using-namespace-directives 导 入的,指明为 identifier 名 字的扩展方法 处理重写的方法调用。第一个产生非空候选方法集的方法组(method group)就成为被选中的重写的方法调用。如果所有的努力都只产生空的候选集,则发生编译期错误。
前面的规则标表明实例方法优先于扩展方法,并且导入进内层名字空间中的扩展方法优先于导入进外层名字空间中的扩展方法。例如:
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)
}
}
例子中,B的方法优先于第一个扩展方法,C的方法优先于两个扩展方法。
【26.3 Lambda表达式】
C# 2.0 引入了匿名方法,它允许在delegate值(delegate value) (译者:delegate对象)被需要的地方以内联(in-line)方式写一个代码块。当匿名方法提供了大量函数式编程语言(或泛函编程)(functionalprogramming)的表达力时,实质上,匿名方法的语法是相当烦琐和带有强制性的。Lambda表达式提供了一个更加简练的函数式语法来写匿名方法。
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
6 Copyright 2 Microsoft Corporation 2005. All Rights Reserved.
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
Lambda表达式的参数可以是显型和隐型的。在显型参数列表中,每个参数的类型是显式指定的。在隐型参数列表中,参数的类型由lambda表达式出现的语境推导——特定地,当lambda表达式被转型到一个兼容的delegate类型时,delegate类型提供参数的类型(§)。在有单一的隐型参数的lambda表达式中,圆括号可以从参数列表中省略。换句话说,如下形式的
lambda表达式
( param ) => expr
可以被简写成
param => expr
下面是一些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
通常,C# 2.0规范§21中提供的匿名方法规范,也应用上了lambda表达式。Lambda表达式是匿名方法的泛函超集,它提供了如下附加功能:
 Lambda表达式允许参数类型被省略掉和被推导,尽管匿名方法要求显式指定参数类型。
 Lambda表达式体可以是一个表达式或者语句块,尽管匿名方法体可以是一个语句块。
 Lambda表达式作为参数传递参与类型参数推导(§26.3.3)和重载决议。
 带有表达式体的Lambda表达式可以被转换成表达式树(§26.8)。
注意
PDC 2005技术预览编译器不支持带有语句体的lambda表达式。在需要语句体的情况下,必须使用C# 2.0匿名方法语法。
【26.3.1 Lambda表达式转换】
与匿名方法表达式(anonymous-method-expression)类似,lambda表达式是用特殊转换规则作为值(value)类型分类的。这个值(value)没有类型,但是可以隐式转型至一个兼容的delegate类型。特别地,delegate类型D与lambda表达式L兼容的,如果:
 D和L有相同数目的参数。
 如果L有显型参数列表,D中的每个参数有着与相应的L中的参数相同的类型和修饰符。
 如果L有隐型参数列表,D不可有ref或out参数。
 如果D有void 返回类型,并且L的体(body)是一个表达式,当L的每个参数被给定为对应的 D 中 参数的类型时,L的体是一个允许作为语句-表达式(statement-expression(§8.6))的有效表达式
 如果D有void返回类型并且L的体是语句块,当L的每个参数类型是被给定为相应的D参数的类型时,L的体是一个没有返回语句的有效语句块。
 如果D有non-void返回值并且L的体是一个表达式,当L的每个参数类型是被给定的相应于D参数的类型时,L的体是一个可以隐式转换到D返回类型的有效表达式。
 如果D有non-void返回值并且L的体是一个语句块,当L的每个参数类型是被给定的相应于D参数的类型时,L的体是一个有效的语句块,语句块中 有不可到达 (non-reachable) 的终点 (end poin t)(译者:
是否应该为“没有不可到达的终点”),且每个终点的返回语句指明 一个可以隐式转换到 D 返 回类型的表达式。
下面的例子使用泛型delegagte类型Func<A,R>表示一个带有参数类型A和返回类型R的函数:
delegate R Func<A,R>(A arg);
赋值如下:
Func<int,int> f1 = x => x + 1; // Ok
Func<int,double> f2 = x => x + 1; // Ok
Func<double,int> f3 = x => x + 1; // Error
每个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 类型推导】
当泛型方法被调用而不指明类型参数时,参数推导过程试图从调用中推导出类型参数。Lambda表达式参数传递给泛型方法参与这个类型推导过程。
如同§20.6.4中表述的那样,类型推导首先为每个参数独立的发生。在初始阶段,不能从lambda表达式参数推导出任何东西。然而,初始阶段之后,产生了使用迭代过程的额外的推导。特别地,只要有一个或多个满足如下条件为真的参数存在,推导将会产生:
 参数是lambda表达式,下面称为L,从中,尚无推导。
 相应的参数类型,下面称为P,是有返回类型的含有一个或多个方法类型参数的delegate。
 P和L拥有相同数目的参数,并且P中的每个参数与L中相应的参数具有相同的修饰符,或者如果L有隐型参数列表时,没有修饰符。
 P的参数类型不包含方法类型参数或者包含仅仅一个方法类型参数,对这个参数已经产生一个相容的推导集。
 如果L有一个显型参数列表,当推导出的类型对于P中的方法类型参数是可替换的时候,P中的每个参数拥有与L中对应的参数相同的类型。
 如果L有一个隐型参数列表,当推导出的类型对于P中的方法类型参数是可替代的,并且返回参数类型被给予L的参数,L的体是一个有效表达式或语句块。
 返回类型可以为L推导出来,描述如下:
对每一个这样的参数,将会通过关联P的返回类型和L的推导返回类型做出推论,并且新的推论被加入进累积的推论集。这个过程将重复进行,直到没有更进一步的推论产生为止。
因为类型推导和重载决议的原因,lambda表达式L推导出的类型决定于下面:
 如果L的体是一个表达式,表达式的类型就是推导出的L的返回类型。
 如果L的体是一个语句块,如果由 语句块中 return 语 句表达式的类型 形成的集合(set)正好包含一个集合中每个类型都可隐式转换成的类型,那么这个类型就是推导出的L的返回类型。(译者:如果有个集合{int, byte, double},则double满足要求)
 此外,返回类型不能为L推导出来。
作为一个包含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);
}
}
}
假定System.Query名字空间使用using子句导入,并且给出一个类Customer,带有类型为string的属性Name, Select方法可用作选择 一列 (list of )customers 的 名字List<Customer> customers = GetCustomerList();
IEnumerable<string> names = customers.Select(c => c.Name);
Select扩展方法调用通过重写静态方法调用处理:
IEnumerable<string> names = Sequence.Select(customers, c => c.Name);
因为类型参数未被显式指明,将会使用类型推导来推导类型参数。首先customers参数被关联到source参数,推导T是Customer。然后使用前面描述的lambda表达式类型推导过程, c是给定类型Customer,而表达式c.Name被关联到selector参数的返回类型上,推导s是string,这样,调用就等价于Sequence.Select<Customer,string>(customers, (Customer c) => c.Name)
返回类型是IEnumerable<string>。
下面的例子示范了lambda表达式类型推导是如何允许类型信息在泛型函数调用的参数之间“流动”的。
给出方法
static Z F<X,Y,Z>(X value, Func<X,Y> f1, Func<Y,Z> f2) {
return f2(f1(value));
}
调用的类型推导
double seconds = F("1:15:30", s => TimeSpan.Parse(s), t => t.TotalSeconds);
Copyright 2 Microsoft Corporation 2005. All Rights Reserved. 9
Overview of C# 3.0
处理过程如下:首先参数”1:15:30”被关联到值参数,推导X是string。然后第一个lambda表达式的参数s是给定推导类型string,并且表达式TimeSpan.Parse(s)被关联到f1的返回类型上,推导Y为System.TimeSpan。最后第二个lambda表达式的参数t,是给定为推导类型System.TimeSpan,表达式t.ToTalSeconds被关联到f2的返回类型上,推导Z为double。这样,调用的返回类型就是double。
【26.3.3 Overload resolution 重载决议】
参数列表中的Lambda表达式在某些条件下影响重载决议。
下面的规则要增加进§7.4.2.3:给定一个lambda表达式L,为其推导出的返回类型存在,如果delegate类型D1 和D2具有相同的参数列表,从 L 到 D 1 的 隐式转型比从 L 到 D2的隐式转型更好;并且从 L 推 导出的返回类型到D1返回类型的隐式转型比从L推导出的返回类型到D2返回类型的隐式转型更好。如果这些条件不为真,两者都不行。
下面的例子例示了这个规则的效果。
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>类有两个Sum方法。每个方法都有一个selector参数,方法从列表项中提取值累加进sum。提取的值可以是int或double型,返回sum同样可以是int或double型。Sum方法可以作为例子用于从detail列表中依次计算和。
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);
...
}
orderDetails.Sum首次调用中,两个Sum方法都适用,这是因为lambda表达式d=>d.UnitCount兼容于
Func<Detail,int>和Func<Detail,double>这两者。然而,重载决议选择了第一个Sum方法,这是因为转换至Func<Detail,in>好于转换至Func<Detail,double>。orderDetails.Sum的第二次调用中,仅仅第二个Sum方法适用,这是因为lambda表达式d=>d.UnitPrice*d.UnitCount产生的类型是double。因此重载决议为此调用选择了第二个方法。
【26.4 对象和集合初始化器】
对象创建表达式(§7.5.10.1)可以包含一个对象或集合初始化器,用于初始化新创建的对象的成员或新创建的集合的元素。

object-creation-expression:
new type ( argument-listopt ) object-or-collection-initializeropt
new type object-or-collection-initializer
object-or-collection-initializer:
object-initializer
collection-initializer
对象创建表达式可以省略构造函数(译者:或译“构造器”)(constructor)的参数列表和封闭的圆括号,而提供给它一个对象或集合初始化器。省略构造函数参数列表和封闭的圆括号等价于指定一个空参数列表。包含对象或集合初始化器的对象创建表达式的执行包含首先调用实例构造函数,然后执行由对象或集合初始化器指定的成员或元素初始化动作。
对象或集合初始化器不能引用正被实例化的对象实例。
【26.4.1 Object initializers 对象初始化器】
对象初始化器指定一个或多个对象的域或属性的 值。
object-initializer:
{ member-initializer-listopt }
{ member-initializer-list , }
member-initializer-list:
member-initializer
member-initializer-list , member-initializer
member-initializer:
identifier = initializer-value
initializer-value:
expression
object-or-collection-initializer
对象初始化器由一系列成员初始化器组成,封闭于{和}标记内并且由逗号间隔。每个成员初始化器必须指出正被初始化的对象的域或属性的名字,后面是等号”=”和 表达式或者 对象或集合的 初始化器 。
在等号后面指定表达式的成员初始化器作为与对域或属性赋值同样的方式处理。
在等号后指定一个对象初始化器的成员初始化器是对内嵌对象的初始化。对象初始化器中的赋值作为域或属性成员的赋值对待,而不是给域或属性赋予新值。值类型的属性不可用这种构造方式初始化。
在等号后指定集合初始化器的成员初始化器是对内嵌集合的初始化。初始化器中给定的元素被加进域或属性引用的集合中,而不是给域或属性赋予新的集合。域或属性必须是满足§中指定要求的集合类型。
下面的类要求一个有两个坐标的point:
public class Point
{
int x, y;
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
}
Point的实例可以被创建和实例化如下:
var a = new Point { X = 0, Y = 1 };
它等效于
var a = new Point();
a.X = 0;
a.Y = 1;
下面的这个类表示由两个points构成的rectangle。
public class Rectangle
{
Point p1, p2;
public Point P1 { get { return p1; } set { p1 = value; } }
public Point P2 { get { return p2; } set { p2 = value; } }
}
Rectangle的实例可以被创建和初始化如下:
var r = new Rectangle {
P1 = new Point { X = 0, Y = 1 },
P2 = new Point { X = 2, Y = 3 }
};
它等效于
var r = new Rectangle();
var __p1 = new Point();
__p1.X = 0;
__p1.Y = 1;
r.P1 = __p1;
var __p2 = new Point();
__p2.X = 2;
__p2.Y = 3;
r.P2 = __p2;
这里__p1和__p2是临时变量且是不可见和不可访问的。
如果Rectangle的构造函数分配了两个内嵌的Point的实例
public class Rectangle
{
Point p1 = new Point();
Point p2 = new Point();
public Point P1 { get { return p1; } }
public Point P2 { get { return p2; } }
}
下面的构造可被用于初始化内嵌的Point实例,而不是赋予新的实例值。
var r = new Rectangle {
P1 = { X = 0, Y = 1 },
P2 = { X = 2, Y = 3 }
};
它等效于
var r = new Rectangle();
r.P1.X = 0;
r.P1.Y = 1;
r.P2.X = 2;
r.P2.Y = 3;

【26.4.2 集合初始化器】
集合初始化器指定集合的元素。
collection-initializer:
{ element-initializer-listopt }
{ element-initializer-list , }
element-initializer-list:
element-initializer
element-initializer-list , element-initializer
element-initializer:
non-assignment-expression
集合初始化器由一系列元素初始化器组成,封闭进 { 和 } 标记内,以逗号间隔。每个元素初始化器指定一个将被加进正被初始化的集合对象中的元素。下面是对象创建表达式的例子,包含有一个集合初始化器:
List<int> digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
被应用了集合初始化器的集合对象必须是 实现了正好一个类型 T 的
System.Collections.Generic.IConlection<T> 的 类型。此外必须存在从每个元素类型到T类型的隐式转型。如果这些条件都不满足,就产生编译期错误。集合初始化器对每个指定元素依次调用。
ICollection<T>.Add(T)方法。
下面的类表示一个名字和电话号码列表的contact。
public class Contact
{
string name;
List<string> phoneNumbers = new List<string>();
public string Name { get { return name; } set { name = value; } }
public List<string> PhoneNumbers { get { return phoneNumbers; } }
}
List<Contact>可以被创建和实例化如下:
var contacts = new List<Contact> {
new Contact {
Name = "Chris Smith",
PhoneNumbers = { "206-555-0101", "425-882-8080" }
},
new Contact {
Name = "Bob Harris",
PhoneNumbers = { "650-555-0199" }
}
};
它等效于:
Copyright 2 Microsoft Corporation 2005. All Rights Reserved. 13
Overview of C# 3.0
var contacts = new List<Contact>();
var __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
contacts.Add(__c1);
var __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
contacts.Add(__c2);
这里__c1和__c2是临时变量,不可见,也不可访问。
【26.5 匿名类型】
C# 3.0允许new操作符与匿名对象初始化器联用来创建一个匿名类型的对象。
primary-no-array-creation-expression:

anonymous-object-creation-expression
anonymous-object-creation-expression:
new anonymous-object-initializer
anonymous-object-initializer:
{ member-declarator-listopt }
{ member-declarator-list , }
member-declarator-list:
member-declarator
member-declarator-list , member-declarator
member-declarator:
simple-name
member-access
identifier = expression
匿名对象初始化器声明一个匿名类型并返回这个类型的实例。一个匿名类型是一个无名类(nameless class)(译者:参考jjhou先生的翻译“具名(named)”),它直接继承自Object。匿名类型的成员是一系列推导自用于创建这个类型实例的对象初始化器的读/写属性。特别地,匿名对象初始化器具有如下形式:
new { p1 = e1 , p2 = e2 , … pn = en }
它声明了一个如下形式的匿名类型
class __Anonymous1
{
private T1 f1 ;
private T2 f2 ;

private Tn fn ;
public T1 p1 { get { return f1 ; } set { f1 = value ; } }
public T2 p2 { get { return f2 ; } set { f2 = value ; } }

public T1 p1 { get { return f1 ; } set { f1 = value ; } }
}
这里每个Tx是对应表达式ex的类型。匿名对象初始化器中的表达式是null类型是一个编译期错误。
匿名类型的名字是由编译器自动产生的,在程序正文中不可被引用。
在同样的程序中,以相同顺序指定了一系列相同名字和类型的两个匿名对象初始化器将会产生相同匿名类型的实例。(这个定义包含了属性的次序,是因为它在某些环境中这是可观测和重要的,比如reflection)
例子
var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;
最后一行的赋值是可行的,因为p1和 p2具有相同的匿名类型。
成员声明符可以缩写成简单的名字(§7.5.2)或一个成员访问(§7.5.4)。这称为投射初始化器(projection initializer),是具备相同名字属性声明和赋值的速记方式。
identifier expr . identifier
分别等价于下面:
identifer = identifier identifier = expr . identifier
因此,在投射初始化器中,identifier选择了被赋予值的值和域或属性。直观上,投射初始化器不仅投射
值,也投射值的名字。
【26.6 隐型数组(Implicitly typed arrarys)】
扩展 数组创建表达式(§7.5.10.2)的语法用以支持隐型数组创建表达式:
array-creation-expression:

new [ ] array-initializer
在隐型数组创建表达式中,数组实例的类型推导自数组初始化器中元素的类型。特别地,数组初始化器中表达式类型形成的类型集合(set),必须包含一个这样的类型,其中每个类型都可隐式转型成它,并且这个类型不是null类型,如此,这个类型的数组就被创建了。如果不能推导出一个准确的类型,或者推导出的类型是空null类型,编译器错误就会出现。
下面是隐型数组创建表达式的例子:
var a = new[] { 1, 10, 100, 1000 }; // int[]
var b = new[] { 1, 1.5, 2, 2.5 }; // double[]
var c = new[] { "hello", null, "world” }; // string[]
var d = new[] { 1, "one", 2, "two" }; // Error
最后一个表达式导致编译器错误,这是因为int和string都不能隐式转换成对方。显型数组创建表达式必须这么使用:例如指定类型为object[]。另外,其中一个元素可以被转型到一个公共基类型,这个类型就会成为推导出的元素类型。
隐型数组创建表达式可以与匿名对象初始化器结合使用来创建匿名类型的数据结构。例如:
var contacts = new[] {
new {
Name = "Chris Smith",
PhoneNumbers = new[] { "206-555-0101", "425-882-8080" }
},
new {
Name = "Bob Harris",
PhoneNumbers = new[] { "650-555-0199" }
}
};
【26.7 查询表达式】
查询表达式为查询提供了一个 类似于关系和分层的查询语言 ( 如 S QL 和 X Query) 的 语言集成(译者:
intergrated或译“整合”)语法。
query-expression:
from-clause query-body
from-clause:
from from-generators
from-generators:
from-generator
from-generators , from-generator
from-generator:
identifier in expression
query-body:
from-or-where-clausesopt orderby-clauseopt select-or-group-clause into-clauseopt
from-or-where-clauses:
from-or-where-clause
from-or-where-clauses from-or-where-clause
from-or-where-clause:
from-clause
where-clause
where-clause:
where boolean-expression
orderby-clause:
orderby ordering-clauses
ordering-clauses:
ordering-clause
ordering-clauses , ordering-clause
ordering-clause:
expression ordering-directionopt
ordering-direction:
ascending
descending
16 Copyright 2 Microsoft Corporation 2005. All Rights Reserved.
select-or-group-clause:
select-clause
group-clause
select-clause:
select expression
group-clause:
group expression by expression
into-clause:
into identifier query-body
查询表达式作为非赋值(non-assignment-expression)表达式分类,其定义出现在§。查询表达式以from开始,结束于select或group子句。开头的from子句可以跟随0个或者更多个from或where子句。每个from子句都是一个产生器,它引入了一个迭代变量在序列上搜索;每个where子句是一个过滤器,它从结果中排除一些项。最后的select或group子句指定了依据迭代变量得出的结果的外形(shape)。Select或group子句前面可有一个orderby子句,它指明返回结果的顺序。最后into子句可以通过把一条查询语句的结果作为产生器插进子序列查询中的方式来拼接查询。
在查询表达式中,多个产生器的from子句正好等价于多个 连续的带有单个产生器的 f rom 子 句。
【26.7.1 查询表达式translation】
C# 3.0语言没有指定查询表达式准确的执行语义。然而C# 3.0 把查询表达式转换(translate)成遵循查询表达式模式的多个方法的调用。特别地,查询表达式被转换成名为Where, Select, SelectMany, OrderBy,
OrderByDescending, ThenBy, ThenByDescending和GroupBy的方法调用,这些方法预期拥有特别的签名和返回类型,描述于§。这些方法可以是被查询对象的实例方法或者对象外部的扩展方法,它们实现了实际上的查询的执行过程。
从查询表达式到方法调用的转换(translation),是发生在任何类型绑定或重载决议执行之前的语法映射。转换要求保证语法上的正确,但不保证产生语义正确的C#代码。查询表达式的转换之后,产生的方法调用作为常规函数调用被处理,并且这可能依次暴露出错误。比如如果方法不存在,再比如参数类型错误或者方法是泛型的而类型推导失败。
查询表达式的转换通过一系列例子示范如下。正式的转换规则的描述在后面部分。
【26.7.1.1 where子句】
查询表达式中的where子句:
from c in customers
where c.City == "London"
select c
转换成带有 通过结合迭代变量标识符和 w here 子 句表达式合成的 l ambda 表 达式 的Where方法。
customers.
Where(c => c.City == "London")
【26.7.1.2 select子句】
前面部分的例子示范了 选择最内层迭代变量的 selec t 子 句是如何通过转换成方法调用而被移除的。
Select子句选择最内层迭代变量以外的东西:
from c in customers
where c.City == "London"
select c.Name
转换成合成的lambda表达式的Select方法的调用:
customers.
Where(c => c.City == "London").
Select(c => c.Name)
【26.7.1.3 group子句】
group子句:
from c in customers
group c.Name by c.Country
转换成GroupBy方法的调用
customers.
GroupBy(c => c.Country, c => c.Name)
【26.7.1.4 orderby子句】
orderby子句:
from c in customers
orderby c.Name
select new { c.Name, c.Phone }
转换成OrderBy方法的调用,或者如果递减方向被指定时,转换成OrderByDescending方法的调用。
customers.
OrderBy(c => c.Name).
Select(c => new { c.Name, c.Phone })
orderby子句中的第二个(secondary)次序
from c in customers
orderby c.Country, c.Balance descending
select new { c.Name, c.Country, c.Balance }
转换成对ThenBy和ThenByDescending方法的调用:
customers.
OrderBy(c => c.Country).
ThenByDescending(c => c.Balance).
Select(c => new { c.Name, c.Country, c.Balance })
【26.7.1.5 多重产生器】
多重产生器:
from c in customers
where c.City == "London"
from o in c.Orders
where o.OrderDate.Year == 2005
select new { c.Name, o.OrderID, o.Total }
除了最内层产生器之外,所有的都转换成对SelectMany的调用,:
customers.
Where(c => c.City == "London").
SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new { c.Name, o.OrderID, o.Total })
)
当多重产生器与orderby子句结合时:
from c in customers, o in c.Orders
where o.OrderDate.Year == 2005
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total }
额外的Select被注入进来搜集排序表达式(ordering expressions)和元组序列中的最终结果。 有必要这样做,以至OrderBy可以操作于整个序列上。OrderBy之后,最终结果从元组中提取出来。
customers.
SelectMany(c =>
c.Orders.
Where(o => o.OrderDate.Year == 2005).
Select(o => new { k1 = o.Total, v = new { c.Name, o.OrderID, o.Total } })
).
OrderByDescending(x => x.k1).
Select(x => x.v)
【26.7.1.6 info子句】
info子句:
from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Group.Count() }
它是内嵌查询的一个简单而更方便的表示法:
from g in
from c in customers
group c by c.Country
select new { Country = g.Key, CustCount = g.Group.Count() }
转换如下:
customers.
GroupBy(c => c.Country).
Select(g => new { Country = g.Key, CustCount = g.Group.Count() })
【26.7.2 查询表达式模式】
查询表达式模式建立了一个方法的模式,类型可以实现它来支持查询表达式。因为查询表达式利用语法映射转变成方法调用,类型在如何实现查询表达式模式时有很大的灵活性。例如:模式的方法可以被实现成实例方法或扩展方法,因为这两者具有同样的调用语法;并且方法可以要求(request)delegates或表达式树,因为lambda可以转换成这两者。
推荐使用的支持查询表达式模式的泛型类型 C <T> 的外形(shape)显示如下。泛型类型被使用来例示参数和返回类型的适当关系,但是非泛型类型实现这个模式同样是可能的。
delegate R Func<A,R>(A arg);
class C<T>
{
public C<T> Where(Func<T,bool> predicate);
public C<S> Select<S>(Func<T,S> selector);
public C<S> SelectMany<S>(Func<T,C<S>> selector);
public O<T> OrderBy<K>(Func<T,K> keyExpr);
public O<T> OrderByDescending<K>(Func<T,K> keyExpr);
public C<G<K,T>> GroupBy<K>(Func<T,K> keyExpr);
Copyright 2 Microsoft Corporation 2005. All Rights Reserved. 19
Overview of C# 3.0
public C<G<K,E>> GroupBy<K,E>(Func<T,K> keyExpr, Func<T,E> elemExpr);
}
class O<T> : C<T>
{
public O<T> ThenBy<K>(Func<T,K> keySelector);
public O<T> ThenByDescending<K>(Func<T,K> keySelector);
}
class G<K,T>
{
public K Key { get; }
public C<T> Group { get; }
}
上面的方法使用泛型delegate类型Func<A, R>,但是同样可以以参数和返回类型中的相同关系来使用其
它delegate或表达式树类型。
注意,推荐的C<T>和O<T>之间的关系保证仅在 OrderBy 或 O rderByDescending 的 返回结果 上ThenBy
和ThenByDescending方法是可用的。也请注意,推荐的GroupBy结果的外形是分组的序列,每个分组
具有Key和Group属性(property)。
标准的查询操作符 (描述于单独的规范中)提供 任意实现了 S ystem.Collections.Generic.IEnumerable<T>
接口的查询操作符模式的实现。
【26.7.3 正式的转换规则】
查询表达式通过依次重复应用下述转换进行处理。每个转换被应用,直到不出现指定的模式为止。
注意,在产生OrderBy和ThenBy调用的转换中,如果对应的排序子句指定了一个递减方向的指示器,
OrderByDescending或ThenByDescending就会产生。
 包含into子句的查询
q1 into x q2
转换成
from x in ( q1 ) q2
 带有多个产生器的from子句
from g1 , g2 , … gn
转换成
from g1 from g2 … from gn
 立即跟随where子句的form子句
from x in e where f
被转换成
from x in ( e ) . Where ( x => f )
 多个from,一个orderby和一个select子句的查询表达式
from x1 in e1 from x2 in e2 … orderby k1 , k2 … select v
被转换成

( from x1 in e1 from x2 in e2 …
select new { k1 = k1 , k2 = k2 … , v = v } )
. OrderBy ( x => x . k1 ) . ThenBy ( x => x . k2 ) …
. Select ( x => x . v )
 多个from,一个orderby和一个group子句的查询表达式
from x1 in e1 from x2 in e2 … orderby k1 , k2 … group v by g
被转换成
( from x1 in e1 from x2 in e2 …
select new { k1 = k1 , k2 = k2 … , v = v , g = g } )
. OrderBy ( x => x . k1 ) . ThenBy ( x => x . k2 ) …
. GroupBy ( x => x . g , x => x . v )
 多个from和一个select子句的查询表达式
from x in e from x1 in e1 … select v
被转换成
( e ) . SelectMany ( x => from x1 in e1 … select v )
 多个from子句和一个group子句的查询表达式
from x in e from x1 in e1 … group v by g
被转换成
( e ) . SelectMany ( x => from x1 in e1 … group v by g )
 一个from,没有orderby,一个select子句的查询表达式
from x in e select v
被转换成
( e ) . Select ( x => v )
除非当v是标识符x时,转换都是简单的
( e )
 一个from,没有orderby,一个group子句的查询表达式
from x in e group v by g
被翻译成
( e ) . GroupBy ( x => g , x => v )
除了当v是标识符x,转换是
( e ) . GroupBy ( x => g )
Copyright 2 Microsoft Corporation 2005. All Rights Reserved. 21
Overview of C# 3.0
 一个from,一个orderby,一个select子句的查询表达式
from x in e orderby k1 , k2 … select v
被转换成
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) … . Select ( x => v )
除非当v是标识符x时,翻译是简单的。
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) …
 一个from,一个orderby,一个group子句的查询表达式
from x in e orderby k1 , k2 … group v by g
被转换成
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) …
. GroupBy ( x => g , x => v )
除非v是标识符x时,转换是
( e ) . OrderBy ( x => k1 ) . ThenBy ( x => k2 ) … . GroupBy ( x => g )
【26.8 表达式树(Expression trees)】
表达式树允许lambda表达式表示为数据结构而不是可执行代码。可转型到delegate类型D的lambda表达式也可以转型到类型System.Query.Expression<D>。然而,到delegate类型的lambda表达式的转型 导致 建立表达式树实例的代码产生(emit)。表达式树是lambda表达式的高效内存数据表示,并生成表达式转换的结构,且使其显式化。
前面的例子表示一个lambda表达式即作为可执行代码,又作为表达式树。因为存在到Func<int,int>的转型,也存在到Expression<Func<int,int>>的转型。
Func<int,int> f = x => x + 1; // Code
Expression<Func<int,int>> e = x => x + 1; // Data
紧随这些参数之后,delegate f引用返回x+1的方法,表达式树e引用描述表达式x+1的数据结构。
注意:
表达式树的结构将在单独的规范中。这个规范在PDC 2005技术预览会议上还不存在。
原创粉丝点击