C#2.0語(yǔ)言規(guī)范(三)匿名方法
2024-07-21 02:19:51
供稿:網(wǎng)友
第三章 匿名方法
原著:microsoft corporation
原文:http://msdn.microsoft.com/vcsharp/team/language/default.aspx (specificationver2.doc)
翻譯:lover_p
出處:
--------------------------------------------------------------------------------
[內(nèi)容]
3.1 匿名方法表達(dá)式
3.2 匿名方法簽名
3.3 匿名方法轉(zhuǎn)換
3.3.1 委托建立表達(dá)式
3.4 匿名方法塊
3.5 外部變量
3.5.1 捕獲外部變量
3.5.2 局部變量的實(shí)例化
3.6 匿名方法求值
3.7 委托實(shí)例相等性
3.8 明確賦值
3.9 方法組轉(zhuǎn)換
3.10 實(shí)現(xiàn)實(shí)例
3.1 匿名方法表達(dá)式
匿名方法表達(dá)(anonymous-method-expression)式定義了匿名方法(anonymous method),并求得一個(gè)引用了該方法的特殊的值。
primary-no-array-creation-expression:
…
anonymous-method-expression
anonymous-method-expression:
delegate anonymous-method-signatureopt block
anonymous-method-signature:
( anonymous-method-parameter-listopt )
anonymous-method-parameter-list:
anonymous-method-parameter
anonymous-method-parameter-list , anonymous-method-parameter
anonymous-method-parameter:
parameter-modifieropt type identifier
初等非數(shù)組表達(dá)式:
...
匿名方法表達(dá)式
匿名方法表達(dá)式:
delegate 匿名方法簽名可選 塊
匿名方法簽名:
( 匿名方法參數(shù)列表可選 )
匿名方法參數(shù)列表:
匿名方法參數(shù)
匿名方法參數(shù) , 匿名方法參數(shù)
匿名方法參數(shù):
參數(shù)修飾符可選 類型 標(biāo)識(shí)符
匿名方法表達(dá)(anonymous-method-expression)是一個(gè)遵從特殊轉(zhuǎn)換規(guī)則(見3.3)的值。這個(gè)值沒有類型,但可以隱式地轉(zhuǎn)換為一個(gè)兼容的委托類型。
匿名方法表達(dá)式(anonymous-method-expression)為參數(shù)、局部變量和常量以及標(biāo)簽定義了一個(gè)新的聲明空間。
3.2 匿名方法簽名
可選的匿名方法簽名(anonymous-method-signature)為匿名方法的形式參數(shù)定義了名字和類型。這些參數(shù)的作用于是匿名方法的塊(block)。如果一個(gè)局部變量的作用域包含了匿名方法表達(dá)式(anonymous-method-expression),且該匿名方法的參數(shù)和該局部變量相同,則會(huì)產(chǎn)生編譯錯(cuò)誤。
如果一個(gè)匿名方法表達(dá)式(anonymous-method-expression)具有匿名方法簽名(anonymous-method-signature),則與之兼容的委托類型被強(qiáng)制具有相同的參數(shù)類型和修飾符,且具有相同順序(見3.3)。如果一個(gè)匿名方法表達(dá)式(anonymous-method-expression)沒有匿名方法簽名(anonymous-method-signature),則與之相兼容的委托類型被強(qiáng)制要求沒有out參數(shù)。
注意匿名方法簽名(anonymous-method-signature)不能包含特性或參數(shù)數(shù)組(譯注:用于實(shí)現(xiàn)變長(zhǎng)參數(shù)列表)。然而,一個(gè)匿名方法簽名(anonymous-method-signature)可以和一個(gè)包含參數(shù)數(shù)組的委托類型相兼容。
3.3 匿名方法轉(zhuǎn)換
匿名方法表達(dá)式(anonymous-method-expression)是一個(gè)沒有類型的特殊值。一個(gè)匿名方法表達(dá)式(anonymous-method-expression)可以用于委托建立表達(dá)式(delegate-creation-expression)(見3.3.1)。對(duì)于匿名方法表達(dá)式(anonymous-method-expression)的其他有效的應(yīng)用取決于定義于其上的隱式轉(zhuǎn)換。
匿名方法表達(dá)式(anonymous-method-expressio)與任何兼容的委托類型之間均存在隱式轉(zhuǎn)換。如果d是一個(gè)委托類型,而a是一個(gè)匿名方法表達(dá)式(anonymous-method-expression),當(dāng)且僅當(dāng)以下兩個(gè)條件成立的時(shí)候d和a是兼容的。
首先,d的參數(shù)類型必須與a兼容:
如果a不含匿名方法簽名(anonymous-method-signature),則d可以具有任意類型的零或多個(gè)參數(shù),但這些參數(shù)不能帶有out修飾符。
如果具有匿名方法簽名(anonymous-method-signature),則d必須具有和a形同數(shù)量的參數(shù),a中的每個(gè)參數(shù)必須和d中相應(yīng)的參數(shù)具有相同的類型,并且a中每個(gè)參數(shù)上的ref或out修飾符的出現(xiàn)與否必須與d中的相應(yīng)參數(shù)相同。如果d中的最后一個(gè)參數(shù)是參數(shù)數(shù)組,則沒有相互兼容的a和d。
其次,d的返回值類型必須與a兼容。由于這一規(guī)則,a中不能包含其他匿名方法的塊(block)。
如果d的返回值類型被聲明為void,則a中包含的所有return語(yǔ)句不能指定表達(dá)式。
如果d的返回值類型被聲明為r,則a中包含的所有return語(yǔ)句不許指定一個(gè)能夠隱式轉(zhuǎn)換為r的表達(dá)式。a中的塊(block)的終點(diǎn)必須可達(dá)。
除了和相兼容的委托類型之間的隱式轉(zhuǎn)換,匿名方法表達(dá)式(anonymous-method-expression)與任何類型之間不存在任何轉(zhuǎn)換,包括object類型。
下面的例子詳細(xì)地解釋了這些規(guī)則:
delegate void d(int x);
d d1 = delegate { }; // 正確
d d2 = delegate() { }; // 錯(cuò)誤,簽名不匹配
d d3 = delegate(long x) { }; // 錯(cuò)誤,簽名不匹配
d d4 = delegate(int x) { }; // 正確
d d5 = delegate(int x) { return; }; // 正確
d d6 = delegate(int x) { return x; }; // 錯(cuò)誤,返回值不匹配
delegate void e(out int x);
e e1 = delegate { }; // 錯(cuò)誤,e帶有一個(gè)輸出參數(shù)
e e2 = delegate(out int x) { x = 1; }; // 正確
e e3 = delegate(ref int x) { x = 1; }; // 錯(cuò)誤,簽名不匹配
delegate int p(params int[] a);
p p1 = delegate { }; // 錯(cuò)誤,塊的結(jié)尾不可達(dá)
p p2 = delegate { return; }; // 錯(cuò)誤,返回值類型不匹配
p p3 = delegate { return 1; }; // 正確
p p4 = delegate { return "hello"; }; // 錯(cuò)誤,返回值類型不匹配
p p5 = delegate(int[] a) { // 正確
return a[0];
};
p p6 = delegate(params int[] a) { // 錯(cuò)誤,(譯注:多余的)params修飾符
return a[0];
};
p p7 = delegate(int[] a) { // 錯(cuò)誤,返回值類型不匹配
if(a.length > 0) return a[0];
return "hello";
};
delegate object q(params int[] a);
q q1 = delegate(int[] a) { // 正確
if(a.length > 0) return a[0];
return "hello";
};
3.3.1 委托建立表達(dá)式
委托建立表達(dá)式(delegate-creation-expression)可以用于匿名方法和委托類型之間的轉(zhuǎn)換語(yǔ)法的替代品。如果一個(gè)委托建立表達(dá)式(delegate-creation-expression)的參數(shù)表達(dá)式(expression)是一個(gè)匿名方法表達(dá)式(anonymous-method-expression),則匿名方法依照上面定義的隱式轉(zhuǎn)換規(guī)則轉(zhuǎn)換為給定的委托類型。例如,如果d是一個(gè)委托類型,則表達(dá)式
new d(delegate { console.writeline("hello"); })
等價(jià)于表達(dá)式
(d) delegate { console.writeline("hello"); }
3.4 匿名方法塊
匿名方法表達(dá)式(anonymous-method-expression)的塊(block)遵從下列規(guī)則:
如果匿名方法包含一個(gè)簽名,則簽名中指定的參數(shù)在塊(block)中是可用的。如果匿名方法不包含簽名,則它可以轉(zhuǎn)換為一個(gè)帶有參數(shù)的委托類型(見3.3),但這些參數(shù)在塊(block)中無(wú)法訪問。
除非在最貼切的匿名方法的簽名(如果有的話)中指定了ref或out參數(shù),否則在塊中訪問ref或out參數(shù)會(huì)發(fā)生編譯期間錯(cuò)誤。
當(dāng)this的類型是一個(gè)結(jié)構(gòu)類型時(shí),當(dāng)在塊(block)中訪問this是一個(gè)編譯錯(cuò)誤,不論這種能夠訪問是顯式的(如this.x)還是隱式的(如x,而x是該結(jié)構(gòu)的一個(gè)實(shí)例方法)。這一規(guī)則簡(jiǎn)單地禁止了這種訪問,從而不會(huì)對(duì)結(jié)構(gòu)的成員的查找結(jié)果產(chǎn)生影響。
塊(block)可以訪問匿名方法外部的變量(見3.5)。對(duì)外部變量的訪問將會(huì)引用到變量的實(shí)例,該變量在匿名方法表達(dá)式(anonymous-method-expression)求值的過程中應(yīng)當(dāng)是活動(dòng)的(見3.6)。
如果塊(block)中包含的goto語(yǔ)句、break語(yǔ)句或continue語(yǔ)句的目標(biāo)在塊(block)的外面或在塊(block)中包含的一個(gè)匿名方法中,則會(huì)產(chǎn)生編譯錯(cuò)誤。
塊(block)中的return語(yǔ)句將控制從最近的一個(gè)匿名方法的調(diào)用中返回,而不是從函數(shù)成員中返回。return語(yǔ)句中指定的表達(dá)式必須和匿名方法表達(dá)式(anonymous-method-expression)轉(zhuǎn)換(見3.3)得到的委托類型相匹配。
除了計(jì)算和調(diào)用匿名方法表達(dá)式(anonymous-method-expression)外,對(duì)于塊(block)的執(zhí)行方式?jīng)]有任何明確的限制。特別地,編譯器會(huì)選擇通過合成個(gè)或多個(gè)命名了的方法或類型來(lái)實(shí)現(xiàn)一個(gè)匿名方法。這些合成的名字必須保留在編譯器所使用的空間中:這些名字必須包含兩個(gè)連續(xù)的下劃線。
3.5 外部變量
若一個(gè)匿名方法表達(dá)式(anonymous-method-expression)包含在任何局部變量、值參數(shù)或參數(shù)數(shù)組的作用域中,則稱它們(譯注:指那些局部變量、值參數(shù)或參數(shù)數(shù)組)為該匿名方法表達(dá)式(anonymous-method-expression)的外部變量(outer variable)。在一個(gè)類的實(shí)例函數(shù)成員中,this被認(rèn)為是一個(gè)值參數(shù),并且是該函數(shù)成員中所包含的任何匿名方法表達(dá)式(anonymous-method-expression)的外部變量。
3.5.1 捕獲外部變量
當(dāng)在一個(gè)匿名方法中引用一個(gè)外部變量時(shí),稱該外部變量被匿名方法所捕獲。通常,一個(gè)局部變量的生存期是塊或與之關(guān)聯(lián)的語(yǔ)句的執(zhí)行的結(jié)束點(diǎn)。然而,一個(gè)被捕獲的外部變量的生存期將持續(xù)到引用了匿名方法的委托符合垃圾收集的條件時(shí)。
在下面的例子中:
using system;
delegate int d();
class test {
static d f() {
int x = 0;
d result = delegate { return ++x; }
return result;
}
static void main() {
d d = f();
console.writeline(d());
console.writeline(d());
console.writeline(d());
}
}
局部變量x被匿名方法所捕獲,x的生存期至少被延續(xù)到從f所返回的委托符合垃圾收集條件時(shí)(這并不一定發(fā)生在程序的最末尾)。由于每個(gè)匿名方法的調(diào)用均操作了x的同一個(gè)實(shí)例,這個(gè)例子的輸出將會(huì)是:
1
2
3
當(dāng)一個(gè)局部變量或一個(gè)值參數(shù)被一個(gè)匿名方法所捕獲,該局部變量獲知參數(shù)將不再被認(rèn)為是一個(gè)固定變量,而是被認(rèn)為是一個(gè)可移動(dòng)的變量。因此,任何unsafe代碼如果記錄了該外部變量的地址,則必須首先使用fixed語(yǔ)句來(lái)固定這個(gè)變量。
3.5.2 局部變量的實(shí)例化
當(dāng)程序執(zhí)行到一個(gè)變量的作用域中時(shí),則該局部變量被實(shí)例化。例如,當(dāng)下面的方法被調(diào)用時(shí),局部變量x被實(shí)例化和初始化三次——每當(dāng)循環(huán)迭代一次時(shí)。
static void f() {
for(int i = 0; i < 3; i++) {
int x = i * 2 + 1;
...
}
}
然而,將x的聲明移到循環(huán)外面則只會(huì)引起x的一次實(shí)例化:
static void f() {
int x;
for(int i = 0; i < 3; i++) {
x = i * 2 + 1;
...
}
}
通常,沒有辦法觀察到一個(gè)局部變量被實(shí)例化過多少次——因?yàn)閷?shí)例化的生存期是脫節(jié)的,而上每次實(shí)例化都簡(jiǎn)單地使用相同的存貯空間是可能的。然而,當(dāng)一個(gè)匿名方法捕獲了一個(gè)局部變量,實(shí)例化的效果就明顯了。下面的例子
using system;
delegate void d();
class test {
static d[] f() {
d[] result = new d[3];
for(int i = 0; i < 3; i++) {
int x = i * 2 + 1;
result[i] = delegate { console.writeline(x); };
}
return result;
}
static void main() {
foreach (d d in f()) d();
}
}
的輸出為:
1
3
5
然而,當(dāng)x的聲明被移到循環(huán)外面時(shí):
static d[] f() {
d[] result = new d[3];
int x;
for(int i = 0; i < 3; i++) {
x = i * 2 + 1;
result[i] = delegate { console.writeline(x); };
}
return result;
}
結(jié)果為:
5
5
5
注意,根據(jù)判等操作(見3.7),由上面這個(gè)版本的f方法所建立的三個(gè)委托是相等的。另外,編譯器可以(但不是必須)將這三個(gè)實(shí)例優(yōu)化為一個(gè)單獨(dú)的委托實(shí)例(見3.6)。
匿名方法委托可以共享一些捕獲的變量,而具有另一些捕獲變量的單獨(dú)的實(shí)例。例如,如果f變?yōu)?br>
static d[] f() {
d[] result = new d[3];
int x = 0;
for(int i = 0; i < 3; i++) {
int y = 0;
result[i] = delegate { console.writeline("{0} {1}", ++x, ++y); };
}
return result;
}
這三個(gè)委托捕獲了x的相同的(一個(gè))實(shí)例,而捕獲y的單獨(dú)的實(shí)例。輸出為:
1 1
2 1
3 1
單獨(dú)的匿名方法可以捕獲一個(gè)外部變量的相同的實(shí)例。下面的例子中:
using system;
delegate void setter(int value);
delegate int getter();
class test {
static void main() {
int x = 0;
setter s = delegate(int value) { x = value; };
getter g = delegate { return x; };
s(5);
console.writeline(g());
s(10);
console.writeline(g());
}
}
兩個(gè)匿名方法捕獲了局部變量x的相同的實(shí)例,它們可以通過改變量進(jìn)行“通信”。這個(gè)例子的輸出為:
5
10
3.6 匿名方法求值
運(yùn)行時(shí)對(duì)匿名方法表達(dá)式的求值將得到一個(gè)委托實(shí)例,該委托實(shí)例引用了這個(gè)匿名方法和一組活動(dòng)的外部變量的集合(可能為空)。當(dāng)一個(gè)由匿名方法表達(dá)式求得的委托被調(diào)用時(shí),匿名方法體將被執(zhí)行。方法體中的代碼可以使用由委托所引用的一組捕獲了的外部變量。
有匿名方法表達(dá)式得到的委托的調(diào)用鏈表包含一個(gè)唯一的入口點(diǎn)。委托的確切的目標(biāo)對(duì)象和目標(biāo)方法是未定義的。尤其是委托的目標(biāo)對(duì)象是否為null、函數(shù)成員內(nèi)部的this值或其他對(duì)象也是未定義的。
對(duì)帶有相同的一組(可能為空)捕獲的外部變量的語(yǔ)義上一致的匿名方法表達(dá)式的求值可以(但不是必須)返回相同的委托實(shí)例。這里用的術(shù)語(yǔ)“語(yǔ)義上一致的”表示任何情況下對(duì)匿名方法的執(zhí)行對(duì)同樣的參數(shù)應(yīng)該產(chǎn)生同樣的效果。這一規(guī)則允許對(duì)代碼作如下優(yōu)化:
delegate double function(double x);
class test {
static double[] apply(double[] a, function f) {
double[] result = new double[a.length];
for(int i = 0; i < a.length; i++) result[i] = f(a[i]);
return result;
}
static void f(double[] a, double[] b) {
a = apply(a, delegate(double x) { return math.sin(x); });
b = apply(b, delegate(double y) { return math.sin(y); });
...
}
}
由于兩個(gè)匿名方法委托具有相同的一組捕獲的外部變量(為空),而且匿名方法在語(yǔ)義上是一致的,編譯器可以令這兩個(gè)委托引用一個(gè)相同的目標(biāo)方法。甚至對(duì)于這兩個(gè)匿名方法表達(dá)式,編譯器可以返回完全一樣的委托實(shí)例。
3.7 委托實(shí)例相等性
下面的規(guī)則決定著匿名方法委托實(shí)例的判等操作符和object.equals方法的結(jié)果:
從語(yǔ)義上相同的帶有相同一組(也可能是都沒有沒有)被捕獲變量的匿名方法表達(dá)式(anonymous-method-expressions)所產(chǎn)生的委托實(shí)例可以(但不是必須)相等。
從語(yǔ)義上相同的單被捕獲變量不同的的匿名方法表達(dá)式(anonymous-method-expressions)所產(chǎn)生的委托實(shí)例必須不相等。
3.8 明確賦值
對(duì)匿名方法參數(shù)的明確賦值規(guī)則和命名方法(named method,區(qū)別于匿名方法)參數(shù)的明確復(fù)制規(guī)定一樣。也就是說(shuō),引用參數(shù)和值參數(shù)必須同國(guó)明確的賦值進(jìn)行初始化,而輸出參數(shù)可以不比進(jìn)行明確賦值。
另外,如果要在匿名方法正常返回之前使用輸出參數(shù),則輸出參數(shù)必須被明確賦值。
當(dāng)控制轉(zhuǎn)移到匿名方法表達(dá)式的塊中時(shí),對(duì)于一個(gè)外部變量v的明確賦值規(guī)定與匿名方法表達(dá)式之前對(duì)v的明確賦值規(guī)定一樣。
對(duì)于一個(gè)匿名方法后面的變量v的明確賦值規(guī)定和匿名方法表達(dá)式之前的明確賦值規(guī)定相同。
下面的例子:
delegate bool filter(int i);
void f() {
int max;
// 錯(cuò)誤,max沒有被明確賦值
filter f = delegate(int n) { return n < max; }
max = 5;
dowork(f);
}
會(huì)產(chǎn)生一個(gè)編譯錯(cuò)誤,因?yàn)樵谀涿椒暶髦癿ax沒有被明確賦值。下面的例子
delegate void d();
void f() {
int n;
d d = delegate { n = 1; };
d();
// 錯(cuò)誤,n沒有被明確賦值
console.writeline(n);
}
同樣會(huì)產(chǎn)生變異錯(cuò)誤,因?yàn)槟涿椒ㄖ袑?duì)n的賦值不會(huì)影響匿名方法外部對(duì)n的明確賦值。
3.9 方法組轉(zhuǎn)換
和3.3節(jié)中描述的匿名方法隱式轉(zhuǎn)換類似,從一個(gè)方法組到一個(gè)兼容的委托類型之間也存在一個(gè)隱式的轉(zhuǎn)換。
對(duì)于一個(gè)給定的方法組e和一個(gè)委托類型d,如果允許形為new d(e)的委托建立表達(dá)式(見2.9.6),則存在e到d的隱式轉(zhuǎn)換,
下面的例子:
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)造器使用兩個(gè)new運(yùn)算符建立了兩個(gè)委托實(shí)例。隱式方法組轉(zhuǎn)換允許將其寫作更短的形式:
public alertdialog() {
okbutton.click += okclick;
cancelbutton.click += cancelclick;
...
}
與其它隱式和顯式轉(zhuǎn)換相同,轉(zhuǎn)換運(yùn)算符可以顯式地用于一個(gè)特定的轉(zhuǎn)換中。因此,下面的例子:
object obj = new eventhandler(mydialog.okclick);
可以寫作:
object obj = (eventhandler)mydialog.okclick;
方法組和匿名方法表達(dá)式會(huì)影響到重載抉擇,但不會(huì)參與類型推斷。更多細(xì)節(jié)參見2.6.4節(jié)
3.10 實(shí)現(xiàn)實(shí)例
這一節(jié)將討論一些標(biāo)準(zhǔn)c#構(gòu)造中匿名方法的可能的實(shí)現(xiàn)。這里描述的實(shí)現(xiàn)和visual c#編譯器基于相同的原理,但這并不是必須的實(shí)現(xiàn),而只是一種可能。
本節(jié)的最后將給出包含了不同特征的匿名方法的代碼實(shí)例。對(duì)于每個(gè)例子,轉(zhuǎn)換得到的相應(yīng)代碼僅使用了標(biāo)準(zhǔn)c#提供的構(gòu)造。這些例子中,假設(shè)標(biāo)識(shí)符d為下面這樣的委托類型:
public delegate void d();
最簡(jiǎn)單的匿名方法是不捕獲任何外部變量的匿名方法:
class test {
static void f() {
d d = delegate { console.writeline("test"); };
}
}
它會(huì)被翻譯為一個(gè)委托實(shí)例,它引用了一個(gè)編譯器產(chǎn)生的靜態(tài)方法,這個(gè)方法中包含了匿名方法中的代碼:
class test {
static void f() {
d d = new d(__method1);
}
static void __method1() {
console.writeline("test");
}
}
in the following example, the anonymous method references instance members of this:
下面的例子中,你名方法引用了this的實(shí)例成員:
class test {
int x;
void f() {
d d = delegate { console.writeline(x); };
}
}
this can be translated to a compiler generated instance method containing the code of the anonymous method:
它會(huì)被翻譯為一個(gè)編譯器生成的實(shí)例方法,該方法包含了匿名方法中的代碼:
class test {
int x;
void f() {
d d = new d(__method1);
}
void __method1() {
console.writeline(x);
}
}
in this example, the anonymous method captures a local variable:
在這個(gè)例子中,匿名方法捕獲了一個(gè)局部變量:
class test {
void f() {
int y = 123;
d d = delegate { console.writeline(y); };
}
}
the lifetime of the local variable must now be extended to at least the lifetime of the anonymous method delegate. this can be achieved by “l(fā)ifting” the local variable into a field of a compiler generated class. instantiation of the local variable (§21.5.2) then corresponds to creating an instance of the compiler generated class, and accessing the local variable corresponds to accessing a field in the instance of the compiler generated class. furthermore, the anonymous method becomes an instance method of the compiler generated class:
局部變量的生存期現(xiàn)在必須擴(kuò)展為至少持續(xù)到匿名方法委托的生存期結(jié)束。這可以通過將局部變量“提升”到一個(gè)編譯器生成的類的域中來(lái)完成。局部變量的實(shí)例化(見3.5.2)相當(dāng)于建立編譯器生成的類的一個(gè)實(shí)例,而訪問局部變量相當(dāng)于訪問編譯器建立的類的這個(gè)實(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);
}
}
}
finally, the following anonymous method captures this as well as two local variables with different lifetimes:
最后,下面的匿名方法捕獲了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); };
}
}
}
here, a compiler generated class is created for each statement block in which locals are captured such that the locals in the different blocks can have independent lifetimes. an instance of __locals2, the compiler generated class for the inner statement block, contains the local variable z and a field that references an instance of __locals1. an instance of __locals1, the compiler generated class for the outer statement block, contains the local variable y and a field that references this of the enclosing function member. with these data structures it is possible to reach all captured outer variables through an instance of __local2, and the code of the anonymous method can thus be implemented as an instance method of that class.
這里,編譯器為每一個(gè)捕獲了局部變量的語(yǔ)句塊分別都生成了一個(gè)類。因此,那些在不同的塊中所捕獲的局部變量具有獨(dú)立的生存期。編譯器為內(nèi)層語(yǔ)句塊建立的類__locals2的一個(gè)實(shí)例,包含了局部變量z和一個(gè)引用了__locals1的實(shí)例的域。編譯器為外層語(yǔ)句塊建立的類__locals1的一個(gè)實(shí)例,包含了局部變量y和一個(gè)引用了函數(shù)成員所在類的this的一個(gè)域。通過這些數(shù)據(jù)結(jié)構(gòu),可以通過__locals2的一個(gè)實(shí)例來(lái)獲得所有捕獲的外部變量,而匿名方法中的代碼因此被實(shí)現(xiàn)為該類的一個(gè)實(shí)例方法。
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);
}
}
}