《C# Version 3.0 Specification》,Microsoft

C# 3.0语言新特性(语言规范)

2005.09.26  来自:CSDN  lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P

C# 3.0C# Orcas——魔鬼)在C# 2.0的基础上引入了很多语言扩展,用以支持高级别的函数式风格类库的创建和使用。这些扩展使得结构性API构造具有与其他领域(如关系数据库和XML)中查询语言同等的表达能力。这些扩展包括:

具有隐式类型的局部变量
,允许通过用于初始化的表达式来推断局部变量的类型。
扩展方法,使得对一个现存类型的扩展和构造具有附加方法的类型变为现实。
拉姆达(Lambda)表达式,匿名方法的一种进化,为委托类型和表达式树提供了改进的类型推断和转换。
对象初始化器,使得构造和初始化对象变得容易。
匿名类型,由对象初始化器推断和创建出来的类型。
具有隐式类型的数组,从数组初始化器推断出元素类型并进行创建和初始化的数组。
查询表达式,提供了集成的查询语法,与关系、分级查询语言如SQLXQuery类似。
表达式树,允许将拉姆达表达式表现为数据(表达式树),而不是代码(委托)。

C# 3.0语言新特性(语言规范):1 具有隐式类型的局部变量
2005.09.26  来自:CSDN  lover_P
原文:《C# Version 3.0 Specification》,Microsoft
翻译:lover_P

在一个
具有隐式类型的局部变量声明implicitly typed local variable declaration)中,被声明的局部变量的类型是通过初始化该变量的表达式推断出来的。当使用局部变量声指示符符var来代替类型,并且当前作用域内没有名为var的类型时,这个声明便成为一个具有隐式类型的局部变量声明。例如:
var i = 5;
var s = "Hello";
var d = 1.0;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int, Order>();
上面这些具有隐式类型的局部变量声明和下面这些具有显式类型的声明完全一致:
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)必须遵循下列约束:
l          该声明器必须包含初始化器。
l          初始化器必须是一个表达式。该初始化器不能是它自己的对象或集合初始化器(第4部分),但可以是一个包含了对象或集合初始化器的new表达式。
l          初始化器表达式在编译期的类型必须不能为空类型。
l          如果局部变量的声明包含多个声明器,所有的初始化器在编译期都必须具有相同的类型。
下面是不正确的具有隐式类型的局部变量声明示例:
var x;                 // 错误,没有用来推断类型的初始化器
var y = {1, 2, 3}; // 错误,不允许使用集合初始化器
var z = null;        // 错误,不允许出现空类型
出于向下兼容的原因,当一个局部变量声明指示符以var作为类型,但当前作用域中有一个名为var的类型时,这个声明使用的是该类型;然而,(编译器)会针对这种模糊的语义给出一个警告。不过由于var违反了类型名字首字母必须大写这条约定,这种情况应该不大会出现。
for语句的for-initializerusing语句的resource-acquisition可以是一个具有隐式类型的局部变量声明。同样,foreach语句中的迭代变量也可以被声明为具有隐式类型的局部变量,在这种情况下,迭代变量的类型通过待遍历的集合的元素类型来推断。
int[] numbers = {1, 3, 5, 7, 9};
foreach(var n in numbers) Console.WriteLine(n);
在上面的例子中n的类型被推断为int——numbers的元素类型。

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;
其中最后一行中的赋值是允许的,因为p1p2具有相同的匿名类型。
一个成员初始化器可以缩写为一个简单名字或一个成员访问。这时称该成员初始化器为发散性初始化器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
最后一个表达式会产生一个编译期错误,因为intstring都不能隐式地转换为另一个。这时就必须使用一个具有显式类型的数组创建表达式,例如指定其类型为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)为查询提供了一种语言集成的语法,这种语法类似于关系和分级查询语言,如SQLXQuery

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-clauses
opt 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-direction
opt

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子句开始,以一个selectgroup子句结束。起始的from子句后可以跟零个或多个fromwhere子句。每个from子句都是一个生成器,该生成器引入了一个可以覆盖整个序列的迭代变量;而每个where子句都是一个过滤器,该过滤器用于从结果中排出项目。最终的selectgroup子句根据迭代变量来指定结果的表现形式。selectgroup子句前面还可以有一个orderby子句,用以指定结果的顺序。最后,可以用一个into子句通过将一个查询的结果作为一个子查询的生成器来“联结”两个查询。

在查询表达式中,具有多个生成器的from子句严格等价于多个顺序的只具有一个生成器的from子句。

7.1 查询表达式的翻译

C# 3.0语言并没有为查询表达式指定确切的执行语义,而是将查询表达式翻译为对附着于查询表达式模式Query Expression Pattern)的方法的调用。特别地,查询表达式分别被翻译为对名为WhereSelectSelectManyOrderByOrderByDescendingThenByThenByDescendingGroupBy的方法的调用,这些方法有着预期的签名和返回值类型。这些方法既可以是待查询对象的实例方法,也可以是对象外部的扩展方法。这些方法进行着实际的查询工作。

将查询表达式翻译为方法调用的过程是一个语法映射过程,发生在任何类型绑定或重载抉择的执行之前。翻译的结果可以保证语法正确,但不一定保证产生语义正确的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 }

将被翻译为对ThenByThenByDescending方法的调用:

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>之间的关系中,要保证ThenByThenByDescending方法只能用在OrderByOrderByDescending的结果上。同时请注意GroupBy结果的推荐形式,应该是一组具有KeyGroup属性的(匿名类型实例)序列。

标准查询运算符(Standard Query Operators,在另外一个规范中描述)提供了查询表达式的一个实现,这个实现可以用于所有实现了System.Collections.Generic.IEnumerable<T>接口的类型。

7.3 正式的翻译规则

对一个查询表达式的处理将重复、依次地应用下列翻译规则。每个翻译都一直应用这些规则直到不再发生任何给定的模式。

注意将会产生对OrderByThenBy的调用的翻译,如果相应的排序子句制定了descending指示符,将产生对OrderByDescendingThenByDescending的调用。

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技术预览版中并不适用。


你可能感兴趣的:(Microsoft)