21.7委託實例相等性
如下規則適用由匿名方法委託實例的相等運算符(§7.9.8)和object.Equals方法產生的結果。
l 當委託實例是由具有相同被捕獲外部變數集合的語義相同的匿名方法表達式計算而產生時,可以說(但不是必須)它們相等。
l 當委託實例由具有語意不同的匿名方法表達式,或具有不同的被捕獲外部變數集合時,它們決不相等。
21.8明確賦值
匿名方法參數的明確賦值狀態與命名方法是相同的。也就是,引用參數和值參數被明確的賦初值,而輸出參數不用賦初值。並且,輸出參數在匿名方法正常回傳之前必須被明確賦值(§5.1.6)。
當控制轉換到匿名方法表達式的程式區塊時,對外部變數v的明確賦值狀態,與在匿名方法表達式之前的v的明確賦值狀態是相同的。也就是,外部變數的明確賦值將從匿名方法表達式上下文被繼承。在匿名方法程序區塊內,明確賦值將和在普通程式區塊內一樣而得到演繹(§5.3.3)。
在匿名方法表達式之後的變數v的明確賦值狀態與在匿名方法表達式之前它的明確賦值狀態相同。
例如
delegate bool Filter(int i); void F() { int max; // 错误,max没有明确赋值 Filter f = delegate(int n) { return n < max; } max = 5; DoWork(f); }
將產生一個編譯時錯誤,因為max沒有在匿名方法聲明的地方明確賦值。範例
delegate void D(); void F() { int n; D d = delegate { n = 1; }; d(); //错误,n没有明确赋值 Console.WriteLine(n); }
也會產生一個編譯時錯誤,因為匿名方法內n的賦值,對於該匿名方法外部n的明確賦值狀態沒有效果。
21.9方法組轉換
與在§21.3中描述的隱式匿名方法轉換相似,也存在從方法組(§7.1)到相容的委託類型的隱式轉換。
對於給定的方法組E和委託類型D,如果允許new D(E)形式的委託創建表達式(§7.5.10.3 和§20.9.6),那麼就存在從E到D的隱式轉換,且轉換的結果恰好等價於new D(E)。
在以下範例中
using System; using System.Windows.Forms; class AlertDialog { Label message = new Label(); Button okButton = new Button(); Button cancelButton = new Button();` public AlertDialog() { okButton.Click += new EventHandler(OkClick); cancelButton.Click += new EventHandler(CancelClick); ... } void OkClick(object sender, EventArgs e) { ... } void CancelClick(object sender, EventArgs e) { ... } }
建構子用new建立了兩個委託實例。隱式方法組轉換允許將之簡化為
public AlertDialog() { okButton.Click += OkClick; cancelButton.Click += CancelClick; ... }
對於所有其他隱式和顯式的轉換,轉換運算符可以用於明確地執行一個特定的轉換。為此,例
object obj = new EventHandler(myDialog.OkClick);
可被代替寫成如下的樣子。
object obj = (EventHandler)myDialog.OkClick;
方法組合匿名方法表達式可以影響重載決策(overload resolution),但它們並不參與型別推論。請參閱§20.6.4以獲取更詳細的資訊。
21.10實作範例
本節以標準C#的構件形式描述匿名方法的可能實作。這裡描述的實作是基於Microsoft C#編譯器所採用的相同原則,但它絕不是強制性的或唯一可能的實作。
本節的後面部分給出了幾個範例程式碼,它包含了具有不同特性的匿名方法。對於每個例子,我們將提供使用唯一標準C#構件的代碼的對應轉換。在這些例子中,標識符D假定表示如下委託類型。
public delegate void D();
匿名方法的最簡形式就是沒有捕捉外部變數的那個。
class Test { static void F() { D d = delegate { Console.WriteLine("test"); }; } }
這段程式碼可轉換到一個引用編譯器產生的靜態方法的委託實例,而匿名方法的程式碼將會放入到該靜態方法中。 、
class Test { static void F() { D d = new D(__Method1); } static void __Method1() { Console.WriteLine("test"); } }
在下面的範例中,匿名方法引用this的實例成員。
class Test { int x; void F() { D d = delegate { Console.WriteLine(x); }; } }
this可以被轉換到由編譯器產生的包含匿名方法程式碼的實例方法。
class Test { int x; void F() { D d = new D(__Method1); } void __Method1() { Console.WriteLine(x); } }
在這個例子中,匿名方法捕捉了一個局部變數。
class Test { void F() { int y = 123; D d = delegate { Console.WriteLine(y); }; } }
该局部变量的生存期现在至少必须延长到匿名方法委托的生存期为止。这可以通过将局部变量“提升(lifting)”为编译器生成的(compiler-generated)类的字段来完成。局部变量的实例化对应于创建一个编译器生成的类的实例,而访问局部变量将对应于访问编译器生成的类实例的一个字段。并且,匿名方法将成为编译器生成类的实例方法。
class Test { void F() { __locals1 = new __Locals1(); __locals1.y = 123; D d = new D(__locals1.__Method1); } class __Locals1 { public int y; public void __Method1() { Console.WriteLine(y); } } }
最后,如下匿名方法将捕获this,以及具有不同生存期的两个局部变量。
class Test { int x; void F() { int y = 123; for (int i = 0; i < 10; i++) { int z = i * 2; D d = delegate { Console.WriteLine(x + y + z); }; } } }
在这里,编译器将为每个语句块生成类,在这些语句块中局部变量将被捕获,而在不同块中的局部变量将会有独立的生存期。
__Locals2的实例,编译器为内部语句块生成的类,包含局部变量z和引用__Locals1实例的字段。__Locals1的实例,编译器为外部语句块生成的类,包含局部变量y和引用封闭函数成员的this的字段。通过这些数据结构,你可以通过__Locals2的一个实例到达所有被捕获的局部变量,并且匿名方法的代码可以作为那个类的实例方法而实现。
class Test { void F() { __locals1 = new __Locals1(); __locals1.__this = this; __locals1.y = 123; for (int i = 0; i < 10; i++) { __locals2 = new __Locals2(); __locals2.__locals1 = __locals1; __locals2.z = i * 2; D d = new D(__locals2.__Method1); } } class __Locals1 { public Test __this; public int y; } class __Locals2 { public __Locals1 __locals1; public int z; public void __Method1() { Console.WriteLine(__locals1.__this.x + __locals1.y + z); } } }
(匿名方法完)
以上就是C# 2.0 Specification(匿名方法)(二)的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!