2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
C# 3.0(C# Orcas——魔鬼)在C# 2.0的基础上引入了很多语言扩展,用以支持高级别的函数式风格类库的创建和使用。这些扩展使得结构性API构造具有与其他领域(如关系数据库和XML)中查询语言同等的表达能力。这些扩展包括:
具有隐式类型的局部变量,允许通过用于初始化的表达式来推断局部变量的类型。
扩展方法,使得对一个现存类型的扩展和构造具有附加方法的类型变为现实。
拉姆达(Lambda)表达式,匿名方法的一种进化,为委托类型和表达式树提供了改进的类型推断和转换。
对象初始化器,使得构造和初始化对象变得容易。
匿名类型,由对象初始化器推断和创建出来的类型。
具有隐式类型的数组,从数组初始化器推断出元素类型并进行创建和初始化的数组。
查询表达式,提供了集成的查询语法,与关系、分级查询语言如SQL和XQuery类似。
表达式树,允许将拉姆达表达式表现为数据(表达式树),而不是代码(委托)。
C# 3.0语言新特性(语言规范):2 扩展方法
2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
扩展方法(Extension Methods)是一种静态方法,可以通过实例方法的语法进行调用。从最终效果上看,扩展方法使得扩展一个现有类型和构造一个具有附加方法的类型变成了现实。
注意
扩展方法很难发觉,并且比起实例方法在功能性上有很大限制。出于这些原因,我们建议保守地使用扩展方法,仅在实例方法不大可行或根本不可行的时候才使用。
扩展成员的其他类型,如属性、事件和运算符都在考虑之中,但目前并未支持。
2.1 声明扩展方法
扩展方法通过在方法的第一个参数上指定关键字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 ArugmentException();
T[] result = new T[count];
Array.Copy(source, index, result, 0, count);
return result;
}
}
}
扩展方法和正常的静态方法具有完全相同的功能。另外,一旦导入了扩展方法,就可以用调用实例方法的语法来调用扩展方法。
2.2 导入扩展方法
扩展方法使用using-namespace-directives导入。除了导入一个命名空间中的类型以外,一个using-namespace-directive还可以导入一个命名空间中所有的静态类中所有的扩展方法。最后,导入的扩展方法表现为其第一个参数的类型的附加方法,并且其优先级比一般的实例方法低。例如,当使用using-namespace-directive导入了上面例子中的Acme.Utilities命名空间时:
using Acme.Utilities;
就可以使用调用实例方法的语法来调用静态类Extensions中的扩展方法了:
string s = "1234";
int i = s.ToInt32(); // 和Extensions.ToInt32(s)一样
int[] digits = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int[] a = digits.Slice(4, 3); // 和Extensions.Slice(digits, 4, 3)一样
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所提供的可见的扩展方法构成。第一个产生了非空候选方法集合的方法组是对冲洗过的方法调用的一个选择。如果所有的尝试都产生了空的候选方法集合,就会出现一个编译期错误。
上述规则意味着实例方法的优先级胜于扩展方法,并且最后引入的命名空间中的扩展方法的优先级胜于较先引入的命名空间中的扩展方法。例如:
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的方法优先于所有两个扩展方法。
C# 3.0语言新特性(语言规范):4 对象和集合初始化器
2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
一个对象创建表达式可以包含一个对象或集合初始化器,用于初始化新创建的对象的成员或新创建的集合的元素。
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
一个对象创建表达式可以省略构造器参数列表,并将其连同圆括号一起替换为一个对象或集合初始化器。省略构造器参数列表并将其连同圆括号一起替换为一个对象或集合初始化器等价于指定一个空的参数列表。
在执行一个带有对象或集合初始化器的对象创建表达式时,首先调用实例构造器,然后执行对象或集合初始化器指定的成员或元素初始化。
对象或集合初始化器不能引用正在初始化的对象实例。
4.1 对象初始化器
对象初始化器指定了对象的一个或多个域或属性的值。
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
对象初始化器由一系列的成员初始化器构成,包围在{和}记号中,并用逗号进行分隔。每个成员初始化器以对象的一个可访问的域或属性的名字开始,后跟一个等号,之后是一个表达式或一个对象或集合初始化器。如果对象初始化其中包括了对同一个域或属性的多于一个的成员初始化器,将会发生错误。
在等号后面指定了表达式的成员初始化器的处理与域和属性的赋值一致。
在等号后面指定了对象初始化器的成员初始化器也是对一个嵌套对象的初始化。与为域或属性赋一个新值不同,对象初始化器中的赋值被视为对域或属性的成员进行赋值。一个具有值类型的属性不能通过这种构造来进行初始化。
在等号后面指定了集合初始化器的成员初始化器也是对一个嵌套集合的初始化。与为域或属性赋一个新的集合不同,初始化器中给定的元素将被添加到域或属性所引用的集合中。该域或属性必须是一个满足下一节所指定的需求的集合类型。
下面的类表是一个具有两个坐标值的点:
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;
下面的类表是一个具有两个点的矩形:
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;
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 };
可以应用集合初始化器的对象的类型必须实现了System.Collections.Generic.ICollections<T>并指定了确定的T。此外,必须存在从每个元素初始化器的类型到T的隐式转换。如果这些条件不能满足,就会产生一个编译期错误。集合初始化器将依次对每个指定的元素调用ICollection<T>.Add(T)。
下面的类表是一个具有一个名字和一组电话号码的通讯录:
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" }
}
};
其等价于:
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是临时变量,在其他地方不可见且不可访问。
C# 3.0语言新特性(语言规范):5 匿名类型
2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
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
一个匿名对象初始化器声明了一个匿名类型并返回了该类型的一个实例。匿名类型是一个没有名字并且直接继承自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 Tn pn { get { return fn; } set { fn = value; } }
}
其中的每个Tx是对应的表达式ex的类型。如果匿名对象初始化器中的某个表达式具有空类型,会发生一个编译期错误。
匿名类型的名字由编译器自动生成,并且不能在程序文本中引用。
在同一个程序中,两个具有相同名字、相同类型和相同顺序的属性的匿名对象初始化器将产生同一个匿名类型的实例。(这个定义包括了属性的顺序,这是因为在某些环境中顺序是可见的而且是非常重要的,比如反射。)
下面的例子:
var p1 = new { Name = "Lawnmower", Price = 495.00 };
var p2 = new { Name = "Shovel", Price = 26.95 };
p1 = p2;
其中最后一行中的赋值是允许的,因为p1和p2具有相同的匿名类型。
一个成员初始化器可以缩写为一个简单名字或一个成员访问。这时称该成员初始化器为发散性初始化器(Projection Initializer),也是对具有相同名字的属性的声明和赋值的简写。特别地,具有下面形式的成员声明器:
identifier |
expr . identifier |
与下面的对应形式完全等价:
identifier = identifier |
identifier = expr . identifier |
因此,在一个发散性的初始化器中,identifier同时选择了所赋的值的值和域或属性。直观上看,发散性的初始化器反映出的不仅仅是一个值,还包括这个值的名字。
C# 3.0语言新特性(语言规范):6 具有隐式类型的数组
2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
数组创建表达式的语法被扩展为支持具有隐式类型的数组创建表达式:
array-creation-expression:
...
new [ ] array-initializer
在一个具有隐式类型的数组创建表达式中,数组实例的类型通过数组初始化器中指定的元素来推断。特别地,数组初始化器中的表达式类型形成的集合中,必须恰好有一个类型使得其他类型都可以隐式地转换为该类型,并且该类型不是空类型,则创建一个具有该类型的数组。如果无法推断出恰好一个这样的类型,或者推断出来的类型是空类型,会发生一个编译期错误。
下面是具有隐式类型的数组创建表达式的一些例子:
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" }
}
};
C# 3.0语言新特性(语言规范):7 查询表达式
2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
查询表达式(Query Expression)为查询提供了一种语言集成的语法,这种语法类似于关系和分级查询语言,如SQL和XQuery。
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-caluseopt 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
select-or-group-clause:
select-clause
group-clause
select-clause:
selelct expression
group-clause:
group expression by expression
into-clause:
into identifier query-body
一个查询表达式以一个from子句开始,以一个select或group子句结束。起始的from子句后可以跟零个或多个from或where子句。每个from子句都是一个生成器,该生成器引入了一个可以覆盖整个序列的迭代变量;而每个where子句都是一个过滤器,该过滤器用于从结果中排出项目。最终的select或group子句根据迭代变量来指定结果的表现形式。select或group子句前面还可以有一个orderby子句,用以指定结果的顺序。最后,可以用一个into子句通过将一个查询的结果作为一个子查询的生成器来“联结”两个查询。
在查询表达式中,具有多个生成器的from子句严格等价于多个顺序的只具有一个生成器的from子句。
7.1 查询表达式的翻译
C# 3.0语言并没有为查询表达式指定确切的执行语义,而是将查询表达式翻译为对附着于查询表达式模式(Query Expression Pattern)的方法的调用。特别地,查询表达式分别被翻译为对名为Where、Select、SelectMany、OrderBy、OrderByDescending、ThenBy、ThenByDescending和GroupBy的方法的调用,这些方法有着预期的签名和返回值类型。这些方法既可以是待查询对象的实例方法,也可以是对象外部的扩展方法。这些方法进行着实际的查询工作。
将查询表达式翻译为方法调用的过程是一个语法映射过程,发生在任何类型绑定或重载抉择的执行之前。翻译的结果可以保证语法正确,但不一定保证产生语义正确的C#代码。在查询表达式翻译之后,产生的方法调用作为一般的方法调用进行处理,这时会依次发现错误,如方法不存在、参数类型错误或对一个范型方法的类型推断失败等。
后面的一系列示例依次演示了查询表达式的翻译。在后面的某一节中给出了翻译规则的正式描述。
7.1.1 where子句
查询表达式中的一个where子句:
from c in customers
where c.City == "London"
select c
将被翻译为对一个Where方法的调用,其参数为合并了迭代变量和where子句中的表达式所得到的拉姆达表达式:
customers.
Where(c => c.City == "London")
7.1.2 select子句
上面的例子演示了选择了最内部的迭代变量的select子句是如何通过翻译为方法调用被消除的。
一个选择了并非最内部的迭代变量的select子句:
from c in customers
where c.City == "Longdon"
select c.Name
将被翻译为一个Select方法调用,其参数是一个拉姆达表达式:
customers.
Where(c => c.City == "London").
Select(c => c.Name)
7.1.3 group子句
一个group子句:
from c in customers
group c.Name by c.Country
将被翻译为对GroupBy方法的调用:
customers.
GroupBy(c => c.Country, c => c.Name)
7.1.4 orderby子句
一个orderby子句:
from c in customers
orderby c.Name
select new { c.Name, c.Phone }
将被翻译为一个对OrderBy方法的调用,或者当指定了descending指示符时,被翻译为一个对OrderByDescending方法的调用:
customers.
OrderBy(c => c.Name).
Select(c => new { c.Name, c.Phone })
另一个orderby子句:
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 })
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将被注入,用于收集排序表达式和最终的结果序列。让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)
7.1.6 into子句
一个into子句:
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() })
7.2 查询表达式模式
查询表达式模式(Query Expression Pattern)建立了类型可以实现的方法的一套模式,用以支持查询表达式。因为查询表达式会被通过语法映射来翻译为方法调用,因此类型在如何实现其查询表达式模式上尤为灵活。例如,模式的这些方法可以被实现为实例方法或扩展方法,因为两者具有完全一样的调用语法;而方法的参数也可以是委托或表达式树,因为拉姆达表达式可以转换为这两者。
下面给出了支持查询表达式模式的范型类型C<T>的推荐形式。范型类型用于演示参数和结果类型之间正确的关系,也可以将模式实现为非范型类型。
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);
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; }
}
上面的方法是用了一个范型委托类型Func<A, R>,也可以使用等价的其他委托或表达式树类型,只要参数和结果类型之间存在正确的关系即可。
注意在推荐的C<T>和O<T>之间的关系中,要保证ThenBy和ThenByDescending方法只能用在OrderBy或OrderByDescending的结果上。同时请注意GroupBy结果的推荐形式,应该是一组具有Key和Group属性的(匿名类型实例)序列。
标准查询运算符(Standard Query Operators,在另外一个规范中描述)提供了查询表达式的一个实现,这个实现可以用于所有实现了System.Collections.Generic.IEnumerable<T>接口的类型。
7.3 正式的翻译规则
对一个查询表达式的处理将重复、依次地应用下列翻译规则。每个翻译都一直应用这些规则直到不再发生任何给定的模式。
注意将会产生对OrderBy和ThenBy的调用的翻译,如果相应的排序子句制定了descending指示符,将产生对OrderByDescending或ThenByDescending的调用。
l 包含了into子句的查询:
q1 into x q2
将被翻译为:
from x in (q1) q2
l 具有多个生成器的from子句:
from g1, g2, ... gn
将被翻译为:
from g1 from g2 ... from gn
l 后面立即跟有where子句的from子句:
from x in e where f
将被翻译为:
from x in (e).Where(x => f)
l 具有多个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)
l 具有多个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)
l 具有多个from子句和一个select子句的查询表达式:
from x in e from x1 in e1 ... select v
将被翻译为:
(e).SelectMany(x => from x1 in e1 ... select v)
l 具有多个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)
l 具有一个from子句、没有orderby子句,并且具有一个select子句的查询表达式:
from x in e select v
将被翻译为:
(e).Select(x => v)
当v就是标识符x时,翻译将被简化为:
(e)
l 具有一个from子句、没有orderby子句,并且具有一个group子句的查询表达式:
from x in e group v by g
将被翻译为
(e).GroupBy(x => g, x => v)
当v就是标识符x时,翻译将被简化为:
(e).GroupBy(x => g)
l 具有一个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) ...
l 具有一个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)
C# 3.0语言新特性(语言规范):8 表达式树
2005.09.26 来自:CSDN lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P
表达式树允许将拉姆达表达式表现为数据结构而不是可执行代码。一个可以转换为委托类型D的拉姆达表达式也可以转换为一个类型为System.Query.Expression<D>的表达式树。将一个拉姆达表达式转换为委托类型导致可执行代码被委托所生成和引用,而将其转换为一个表达式树类型将导致创建了表达式树实例的代码被发出(Emit)。表达式树是拉姆达表达式的一种高效的内存中(in-memory)数据表现形式,并且使得表达式的结构变得透明和明显。
下面的例子将一个拉姆达表达式分别表现为了可执行代码和表达式树。由于存在到Func<int, int>的转换,因此存在到Expression<Func<int, int>>的转换。
Func<int, int> f = x => x + 1; // 代码
Expression<Func<int, int>> e = x => x + 1; // 数据
在这些赋值完成之后,委托f标识一个返回x + 1的方法,而表达式树e表示一个描述了表达式x + 1的数据结构。
注意
表达式树的结构将被转换为一份单独的规范。该规范在PDC 2005技术预览版中并不适用。