|
21.7委托實(shí)例相等性 如下規(guī)則適用由匿名方法委托實(shí)例的相等運(yùn)算符(§7.9.8)和object.Equals方法產(chǎn)生的結(jié)果。
l 當(dāng)委托實(shí)例是由具有相同被捕獲外部變量集合的語義相同的匿名方法表達(dá)式計(jì)算而產(chǎn)生時(shí),可以說(但不是必須)它們相等。
l 當(dāng)委托實(shí)例由具有語義不同的匿名方法表達(dá)式,或具有不同的被捕獲外部變量集合時(shí),它們決不相等。
21.8明確賦值 匿名方法參數(shù)的明確賦值狀態(tài)與命名方法是相同的。也就是,引用參數(shù)和值參數(shù)被明確的賦初值,而輸出參數(shù)不用賦初值。并且,輸出參數(shù)在匿名方法正常返回之前必須被明確賦值(§5.1.6)。
當(dāng)控制轉(zhuǎn)換到匿名方法表達(dá)式的程序塊時(shí),對外部變量v的明確賦值狀態(tài),與在匿名方法表達(dá)式之前的v的明確賦值狀態(tài)是相同的。也就是,外部變量的明確賦值將從匿名方法表達(dá)式上下文被繼承。在匿名方法程序塊內(nèi),明確賦值將和在普通程序塊內(nèi)一樣而得到演繹(§5.3.3)。
在匿名方法表達(dá)式之后的變量v的明確賦值狀態(tài)與在匿名方法表達(dá)式之前它的明確賦值狀態(tài)相同。
例如
delegate bool Filter(int i);
void F() { int max;
// 錯(cuò)誤,max沒有明確賦值 Filter f = delegate(int n) { return n < max; }
max = 5; DoWork(f); }
將產(chǎn)生一個(gè)編譯時(shí)錯(cuò)誤,因?yàn)閙ax沒有在匿名方法聲明的地方明確賦值。示例
delegate void D();
void F() { int n; D d = delegate { n = 1; };
d();
//錯(cuò)誤,n沒有明確賦值 Console.WriteLine(n); }
也將產(chǎn)生一個(gè)編譯時(shí)錯(cuò)誤,因?yàn)槟涿椒▋?nèi)n的賦值,對于該匿名方法外部n的明確賦值狀態(tài)沒有效果。
21.9方法組轉(zhuǎn)換 與在§21.3中描述的隱式匿名方法轉(zhuǎn)換相似,也存在從方法組(§7.1)到兼容的委托類型的隱式轉(zhuǎn)換。
對于給定的方法組E和委托類型D,如果允許new D(E)形式的委托創(chuàng)建表達(dá)式(§7.5.10.3 和 §20.9.6),那么就存在從E到D的隱式轉(zhuǎn)換,并且轉(zhuǎn)換的結(jié)果恰好等價(jià)于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) { ... } }
構(gòu)造函數(shù)用new創(chuàng)建了兩個(gè)委托實(shí)例。隱式方法組轉(zhuǎn)換允許將之簡化為
public AlertDialog() { okButton.Click += OkClick; cancelButton.Click += CancelClick; ... }
對于所有其他隱式和顯式的轉(zhuǎn)換,轉(zhuǎn)換運(yùn)算符可以用于顯式地執(zhí)行一個(gè)特定的轉(zhuǎn)換。為此,示例
object obj = new EventHandler(myDialog.OkClick);
可被代替寫成如下的樣子。
object obj = (EventHandler)myDialog.OkClick;
方法組合匿名方法表達(dá)式可以影響重載決策(overload resolution),但它們并不參與類型推斷。請參見§20.6.4獲取更詳細(xì)的信息。
21.10實(shí)現(xiàn)例子 本節(jié)以標(biāo)準(zhǔn)C#的構(gòu)件形式描述匿名方法的可能實(shí)現(xiàn)。在這里描述的實(shí)現(xiàn)基于Microsoft C#編譯器所采用的相同原則,但它決不是強(qiáng)制性的或唯一可能的實(shí)現(xiàn)。
本節(jié)的后面部分給出了幾個(gè)示例代碼,它包含了具有不同特性的匿名方法。對于每個(gè)例子,我們將提供使用唯一標(biāo)準(zhǔn)C#構(gòu)件的代碼的對應(yīng)轉(zhuǎn)換。在這些例子中,標(biāo)識符D假定表示如下委托類型。
public delegate void D();
匿名方法的最簡形式就是沒有捕獲外部變量的那個(gè)。
class Test { static void F() { D d = delegate { Console.WriteLine("test"); }; } }
這段代碼可被轉(zhuǎn)換到一個(gè)引用編譯器生成的靜態(tài)方法的委托實(shí)例,而匿名方法的代碼將會(huì)放入到該靜態(tài)方法中。、
class Test { static void F() { D d = new D(__Method1); }
static void __Method1() { Console.WriteLine("test"); } }
在下面的示例中,匿名方法引用this的實(shí)例成員。
class Test { int x;
void F() { D d = delegate { Console.WriteLine(x); }; } }
this可以被轉(zhuǎn)換到由編譯器生成的包含匿名方法代碼的實(shí)例方法。
class Test { int x;
void F() { D d = new D(__Method1); }
void __Method1() { Console.WriteLine(x); } }
在這個(gè)例子中,匿名方法捕獲了一個(gè)局部變量。
class Test { void F() { int y = 123; D d = delegate { Console.WriteLine(y); }; } }
該局部變量的生存期現(xiàn)在至少必須延長到匿名方法委托的生存期為止。這可以通過將局部變量“提升(lifting)”為編譯器生成的(compiler-generated)類的字段來完成。局部變量的實(shí)例化對應(yīng)于創(chuàng)建一個(gè)編譯器生成的類的實(shí)例,而訪問局部變量將對應(yīng)于訪問編譯器生成的類實(shí)例的一個(gè)字段。并且,匿名方法將成為編譯器生成類的實(shí)例方法。
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,以及具有不同生存期的兩個(gè)局部變量。
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); }; } } }
在這里,編譯器將為每個(gè)語句塊生成類,在這些語句塊中局部變量將被捕獲,而在不同塊中的局部變量將會(huì)有獨(dú)立的生存期。
__Locals2的實(shí)例,編譯器為內(nèi)部語句塊生成的類,包含局部變量z和引用__Locals1實(shí)例的字段。__Locals1的實(shí)例,編譯器為外部語句塊生成的類,包含局部變量y和引用封閉函數(shù)成員的this的字段。通過這些數(shù)據(jù)結(jié)構(gòu),你可以通過__Locals2的一個(gè)實(shí)例到達(dá)所有被捕獲的局部變量,并且匿名方法的代碼可以作為那個(gè)類的實(shí)例方法而實(shí)現(xiàn)。
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); } } }
(匿
|