[翻译] C# 3.0语言规范 收藏
? [翻譯] C# 3.0語言規范 收藏 
http://lover_p.cstc.net.cn/lover_P/doc/t-csharp3/ 
1 具有隱式類型的局部變量 
2 擴展方法 
2.1 聲明擴展方法 
2.2 導入擴展方法 
2.3 擴展方法的調用 
3 拉姆達表達式 
3.1 拉姆達表達式轉換 
3.2 類型推斷 
3.3 重載抉擇 
4 對象和集合初始化器 
4.1 對象初始化器 
4.2 集合初始化器 
5 匿名類型 
6 具有隱式類型的數組 
7 查詢表達式 
7.1 查詢表達式的翻譯 
7.1.1 where子句 
7.1.2 select子句 
7.1.3 group子句 
7.1.4 orderby子句 
7.1.5 多重生成器 
7.1.6 into子句 
7.2 查詢表達式模式 
7.3 正式的翻譯規則 
8 表達式樹 
--------------------------------------------------------------------------------
C# 3.0(C# Orcas——魔鬼)在C# 2.0的基礎上引入了很多語言擴展,用以支持高級別的函數式風格類庫的創建和使用。這些擴展使得結構性API構造具有與其他領域(如關系數據庫和XML)中查詢語言同等的表達能力。這些擴展包括:
具有隱式類型的局部變量,允許通過用于初始化的表達式來推斷局部變量的類型。 
擴展方法,使得對一個現存類型的擴展和構造具有附加方法的類型變為現實。 
拉姆達(Lambda)表達式,匿名方法的一種進化,為委托類型和表達式樹提供了改進的類型推斷和轉換。 
對象初始化器,使得構造和初始化對象變得容易。 
匿名類型,由對象初始化器推斷和創建出來的類型。 
具有隱式類型的數組,從數組初始化器推斷出元素類型并進行創建和初始化的數組。 
查詢表達式,提供了集成的查詢語法,與關系、分級查詢語言如SQL和XQuery類似。 
表達式樹,允許將拉姆達表達式表現為數據(表達式樹),而不是代碼(委托)。 
【回頂端】
1 具有隱式類型的局部變量
  在一個具有隱式類型的局部變量聲明(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)必須遵循下列約束:
該聲明器必須包含初始化器。 
初始化器必須是一個表達式。該初始化器不能是它自己的對象或集合初始化器(第4部分),但可以是一個包含了對象或集合初始化器的new表達式。 
初始化器表達式在編譯期的類型必須不能為空類型。 
如果局部變量的聲明包含多個聲明器,所有的初始化器在編譯期都必須具有相同的類型。 
  下面是不正確的具有隱式類型的局部變量聲明示例:
var x; // 錯誤,沒有用來推斷類型的初始化器
var y = {1, 2, 3}; // 錯誤,不允許使用集合初始化器
var z = null; // 錯誤,不允許出現空類型
出于向下兼容的原因,當一個局部變量聲明指示符以var作為類型,但當前作用域中有一個名為var的類型時,這個聲明使用的是該類型;然而,(編譯器)會針對這種模糊的語義給出一個警告。不過由于var違反了類型名字首字母必須大寫這條約定,這種情況應該不大會出現。
for語句的for-initializer和using語句的resource-acquisition可以是一個具有隱式類型的局部變量聲明。同樣,foreach語句中的迭代變量也可以被聲明為具有隱式類型的局部變量,在這種情況下,迭代變量的類型通過待遍歷的集合的元素類型來推斷。
int[] numbers = {1, 3, 5, 7, 9};
foreach(var n in numbers) Console.WriteLine(n);
在上面的例子中n的類型被推斷為int——numbers的元素類型。
【回頂端】
2 擴展方法
  擴展方法(Extension Method)是一種靜態方法,可以通過實例方法的語法進行調用。從最終效果上看,擴展方法使得擴展一個現有類型和構造一個具有附加方法的類型變成了現實。
注意
  擴展方法很難發覺,并且比起實例方法在功能性上有很大限制。出于這些原因,我們建議保守地使用擴展方法,僅在實例方法不大可行或根本不可行的時候才使用。
  擴展成員的其他類型,如屬性、事件和運算符都在考慮之中,但目前并未支持。
【回頂端】
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的方法優先于所有兩個擴展方法。
【回頂端】
3 拉姆達表達式
  C# 2.0中引入了匿名方法,允許在期望出現委托的時候以“內聯(in-line)”的代碼替代之。盡管匿名方法提供了函數式編程語言中的很多表達能力,但匿名方法的語法實在是太羅嗦了,并且很不自然。拉姆達表達式(Lambda expression)為書寫匿名方法提供了一種更加簡單、更加函數化的語法。
拉姆達表達式的書寫方式是一個參數列表后跟=>記號,然后跟一個表達式或一個語句塊。
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
拉姆達表達式的參數可以具有顯式的或隱式的類型。在一個具有顯式類型的參數列表中,每個參數的類型都是顯式聲明的。在一個具有隱式類型的參數列表中,參數的類型是從拉姆達表達式出現的上下文中推斷出來的——具體來說,是當拉姆達表達式被轉換為一個兼容的委托類型時,該委托類型提供了參數的類型。
當拉姆達表達式只有一個具有隱式類型的參數時,參數列表中的括號可以省略。換句話說,下面這種形式的拉姆達表達式:
( param ) => expr
可以簡寫為:
param => expr
下面給出的是拉姆達表達式的一些例子:
x => x + 1? // 隱式類型,以表達式作為拉姆達表達式體
x => { return x + 1; }? // 顯式類型,以語句塊作為拉姆達表達式體
(int x) => x + 1? // 顯式類型,以表達式作為拉姆達表達式體
(int x) => { return x + 1; }? // 顯式類型,以語句塊作為拉姆達表達式體
(x, y) => x * y? // 多個參數
() => Console.WriteLine()? // 沒有參數
通常,C# 2.0規范中提到的匿名方法規范同樣適用于拉姆達表達式。拉姆達表達式是匿名方法在功能行上的超集,提供了下列附加的功能:
拉姆達表達式允許省略參數類型并對其進行推斷,而匿名方法要求參數類型必須顯式地聲明。 
拉姆達表達式體可以是表達式或語句塊,而匿名方法體只能是語句塊。 
在類型參數推導和方法重載抉擇時,拉姆達表達式可以被作為參數傳遞。 
以一個表達式作為表達式體的拉姆達表達式可以被轉換為表達式樹。 
注意
  PDC 2005技術預覽版編譯器并不支持以一個語句塊作為表達式體的拉姆達表達式。當必需一個語句塊時,請使用C# 2.0中的匿名方法語法。
【回頂端】
3.1 拉姆達表達式轉換
  和匿名方法表達式類似,拉姆達表達式可以歸類為一種擁有特定轉換規則的值。這種值沒有類型,但可以被隱式地轉換為一個兼容的委托類型。特別地,當滿足下列條件時,委托類型D兼容于拉姆達表達式L:
D和L具有相同數量的參數。 
如果L具有顯式類型的參數列表,D中每個參數的類型和修飾符必須和L中相應的參數完全一致。 
如果L具有隱式類型的參數列表,則D中不能有ref或out參數。 
如果D具有void返回值類型,并且L的表達式體是一個表達式,若L的每個參數的類型與D的參數一致,則L的表達式體必須是一個可接受為statement-expression的有效表達式。 
如果D具有void返回值類型,并且L的表達式體是一個語句塊,若L的每個參數的類型與D的參數一致,則L的表達式體必須是一個有效語句塊,并且該語句塊中不能有帶有表達式的return語句。 
如果D的返回值類型不是void,并且L的表達式體是一個表達式,若L的每個參數的類型與D的參數一致,則L的表達式體必須是一個可以隱式轉換為D的返回值類型的有效表達式。 
如果D的返回值類型不是void,并且L的表達式體是一個語句塊,若L的每個參數的類型與D的參數一致,則L的表達式體必須是一個有效的語句塊,該語句塊不能有可達的終點(即必須有return語句,譯者注),并且每個return語句中的表達式都必須能夠隱式轉換為D的返回值類型。 
  后面的例子將使用一個范型委托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
每個拉姆達表達式的參數和返回值類型通過將拉姆達達表達式賦給的變量的類型來檢測。第一個賦值將拉姆達表達式成功地轉換為了委托類型Func<int, int>,因為x的類型是int,x + 1是一個有效的表達式,并且可以被隱式地轉換為int。同樣,第二個賦值成功地將拉姆達表達式轉換為了委托類型Func<int, double>,因為x + 1的結果(類型為int)可以被隱式地轉換為double類型。然而,第三個賦值將會產生一個編譯期錯誤,因為x給定的類型是double,x + 1的結果(類型為double)不能被隱式地轉換為int。
【回頂端】
3.2 類型推斷
  當在沒有指定類型參數的情況下調用一個范型方法時,一個類型推斷過程回去嘗試為該調用推斷類型參數。被作為參數傳遞給范型方法的拉姆達表達式也會參與這個類型推斷過程。
最先發生的類型推斷獨立于所有參數。在這個初始階段,不會從作為參數的拉姆達表達式推斷出任何東西。然而,在初始階段之后,將通過一個迭代過程從拉姆達表達式進行推斷。特別地,當下列條件之一為真時將會完成推斷:
參數是一個拉姆達表達式,以后簡稱為L,從其中未得到任何推斷。 
相應參數的類型,以后簡稱為P,是一個委托類型,其返回值類型包括了一個或多個方法類型參數。 
P和L具有相同數量的參數,P中每個參數的修飾符與L中相應的參數一致,或者如果L具有隱式類型的參數列表時,沒有參數修飾符。 
P的參數類型不包含方法類型參數,或僅包含于已經推斷出來的類型參數相兼容的一組類型參數。 
如果L具有顯式類型的參數列表,當推斷出來的類型被P中的方法類型參數取代了時,P中的每個參數應該具有和L中相應參數一致的類型。 
如果L具有隱式類型的參數列表,當推斷出來的類型被P中的方法類型參數取代了并且作為結果的參數類型賦給了L時,L的表達式體必須是一個有效的表達式或語句塊。 
可以為L推斷一個返回值類型。這將在后面描述。 
  對于每一個這樣的參數,都是通過關聯P的返回值類型和從L推斷出的返回值類型來從其上進行推斷的,并且新的推斷將被添加到累積的推斷集合中。這個過程一直重復,直到無法進行更多的推斷為止。
在類型推斷和重載抉擇中,拉姆達表達式L的“推斷出來的返回值類型”通過以下步驟進行檢測:
如果L的表達式體是一個表達式,則該表達式的類型就是L的推斷出來的返回值類型。 
如果L的表達式體是一個語句塊,若由該塊中的return語句中的表達式的類型形成的集合中恰好包含一個類型,使得該集合中的每個類型都能隱式地轉換為該類型,并且該類型不是一個空類型,則該類型即是L的推斷出來的返回值類型。 
除此之外,無法從L推斷出一個返回值類型。 
  作為包含了拉姆達表達式的類型推斷的例子,請考慮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);
??????? }
??? }
}
假設使用using語句導入了System.Query命名空間,并且定義了一個Customer類,具有一個類型為string的屬性Name,Select方法可以用于從一個Customer列表中選擇名字:
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。然后運用上面提到的拉姆達表達式類型推斷過程,C的類型是Customer,表達式c.Name將被關聯到selector參數的返回值類型,因此推斷S是string。因此,這個調用等價于:
Sequence.Select<Customer, string>(customers, (Customer c) => c.Name)
并且其返回值類型為IEnumerable<string>。
下面的例子演示了拉姆達表達式的類型推斷是如何允許類型信息在一個范型方法調用的參數之間“流動”的。對于給定的方法:
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 => TotalSeconds);
的類型推斷過程是這樣的:首先,參數"1:15:30"被關聯到value參數,推斷X為string。然后,第一個拉姆達表達式的參數s具有推斷出來的類型string,表達式TimeSpan.Parse(s)被關聯到f1的返回值類型,推斷Y是System.TimeSpan。最后,第二個拉姆達表達式的參數t具有推斷出來的類型System.TimeSpan,并且表達式t.TotalSeconds被關聯到f2的返回值類型,推斷Z為double。因此這個調用的結果類型是double。
【回頂端】
3.3 重載抉擇
  參數列表中的拉姆達表達式將影響到特定情形下的重載抉擇(也稱重載分析,重載解析等,即從幾個重載方法中選擇最合適的方法進行調用的過程,譯者注)。
下面是新添加的規則:對于拉姆達表達式L,且其具有推斷出來的返回值類型,當委托類型D1和委托類型D2具有完全相同的參數列表,并且將L的推斷出來的返回值類型隱式轉換為D1的返回值類型要優于將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參數,用于從列表項目中依次選取值進行求和。選擇的值或者是int或者是double,結果也相應的是int或double。
可以使用Sum方法來根據一份產品明細表對一個訂單進行求和:
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方法都是可以的,因為拉姆達表達式d => d.UnitCount與Func<Detail, int>和Func<Detail, double>都兼容。然而,重載抉擇選用了第一個Sum方法,因為轉換到Func<Detail, int>要優于轉換到Func<Detail, double>。
在對orderDetails.Sum的第二個調用中,只有第二個Sum方法是可用的,因為拉姆達表達式d => d.UnitPrice * d.UnitCount產生的值的類型是double。因此重載抉擇選用第二個Sum方法進行調用。
【回頂端】
4 對象和集合初始化器
  一個對象創建表達式可以包含一個對象或集合初始化器,用于初始化新創建的對象的成員或新創建的集合的元素。
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是臨時變量,在其他地方不可見且不可訪問。
【回頂端】
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
一個匿名對象初始化器聲明了一個匿名類型并返回了該類型的一個實例。匿名類型是一個沒有名字并且直接繼承自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同時選擇了所賦的值的值和域或屬性。直觀上看,影射初始化器反映出的不僅僅是一個值,還包括這個值的名字。
【回頂端】
6 具有隱式類型的數組
  數組創建表達式的語法被擴展為支持具有隱式類型的數組創建表達式:
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" }
??? }
};
【回頂端】
7 查詢表達式
  查詢表達式(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的調用。
包含了into子句的查詢: 
q1 into x q2
將被翻譯為:
from x in (q1) q2
具有多個生成器的from子句: 
from g1, g2, ... gn
將被翻譯為:
from g1 from g2 ... from gn
后面立即跟有where子句的from子句: 
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)
具有一個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)
【回頂端】
8 表達式樹
  表達式樹允許將拉姆達表達式表現為數據結構而不是可執行代碼。一個可以轉換為委托類型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技術預覽版中并不適用。
?
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/loverP/archive/2005/10/21/511796.aspx
轉載于:https://www.cnblogs.com/leilei03632/articles/1728841.html
總結
以上是生活随笔為你收集整理的[翻译] C# 3.0语言规范 收藏的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Docker:集装箱式“运输”在软件上的
- 下一篇: WebService中文件传输
