人人做人人澡人人爽欧美,国产主播一区二区,久久久精品五月天,羞羞视频在线观看免费

當前位置:蘿卜系統下載站 > 技術開發教程 > 詳細頁面

CLR 中匿名函數的完成原理淺析

CLR 中匿名函數的完成原理淺析

更新時間:2022-07-03 文章作者:未知 信息來源:網絡 閱讀次數:

CLR 中匿名函數的實現原理淺析

C# 2.0中提供了通過delegate實現匿名函數功能,能有效地減少用戶的薄記代碼工作,例如


以下為引用:

...
button1.Click += new EventHandler(button1_Click);
...
void button1_Click(Object sender, EventArgs e) {
// Do something, the button was clicked...
}
...




可以被簡化為直接使用匿名函數構造,如


以下為引用:

...
button1.Click += delegate(Object sender, EventArgs e) {
// Do something, the button was clicked...
}
...




關于匿名函數的使用方法可以參考Jeffrey Richter的Working with Delegates Made Easier with C# 2.0一文。簡要說來就是C#編譯器自動將匿名函數代碼轉移到一個自動命名函數中,將原來需要用戶手工完成的工作自動完成。例如構造一個私有靜態函數,如


以下為引用:

class AClass {
static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem(delegate(Object obj) { Console.WriteLine(obj); }, 5);
}
}




被編譯器自動轉換為


以下為引用:

class AClass {
static void CallbackWithoutNewingADelegateObject() {
ThreadPool.QueueUserWorkItem(new WaitCallback(__AnonymousMethod$00000002), 5);
}

private static void __AnonymousMethod$00000002(Object obj) {
Console.WriteLine(obj);
}
}




而這里自動生成的函數是否為static,編譯器根據使用此函數的地方是否static決定。這也是為什么C# 2.0規范里面禁止使用goto, break和continue語句從一個匿名方法里跳出,或從外面跳入其中的原因,因為他們代碼雖然寫在一個作用域里面,但實際上實現上并不在一起。
更方便的是編譯器可以根據匿名函數使用的情況,自動判斷函數參數,無需用戶在定義時指定,如


以下為引用:

button1.Click += delegate(Object sender, EventArgs e) { MessageBox.Show("The Button was clicked!"); };




在不使用參數時,完全等價于


以下為引用:

button1.Click += delegate { MessageBox.Show("The Button was clicked!"); };





相對于匿名函數的實現來說,比較復雜的是匿名函數對于其父作用域中變量的使用及其實現。MS的Grant Ri在其blog上有一系列的討論文章。
Anonymous Methods, Part 1 of ?
Anonymous Methods, Part 2 of ?
Anonymous Method Part 2 answers

需要解決的問題有兩個:一是不在一個變量作用域中的匿名函數如何訪問父函數和類的變量;二是匿名函數使用到的變量的生命周期必須與其綁定,而不能與父函數的調用生命周期綁定。這兩個問題使得C#編譯器選擇較為復雜的獨立類封裝方式實現匿名函數和相關變量生命周期的管理。

首先,匿名函數使用到的父函數中局部變量,無聊是引用類型還是值類型,都必須從棧變量轉換為堆變量,以便在其作用域外的匿名函數實現代碼可以訪問并控制生命周期。因為棧變量的生命周期與其所有者函數是一致的,所有者函數退出后,其堆棧自動恢復到調用函數前,也就無法完成變量生命周期與函數調用生命周期的解耦。
例如下面這個簡單的匿名函數中,使用了父函數的局部變量,雖然此匿名函數只在父函數里面使用,但C#編譯器還是使用獨立類對其使用到的變量進行了包裝。


以下為引用:

delegate void Delegate1();

public void Method1()
{
int i=0;

Delegate1 d1 = delegate() { i++; };

d1();
}




自動生成的包裝代碼類似如下


以下為引用:

delegate void Delegate1();

private sealed class __LocalsDisplayClass$00000002
{
public int i;

public void __AnonymousMethod$00000001()
{
this.i++;
}
};

public void Method1()
{
__LocalsDisplayClass$00000002 local1 = new __LocalsDisplayClass$00000002();
local1.i = 0;

Delegate1 d1 = new Delegate1(local1.__AnonymousMethod$00000001);

d1();
}




但對于有多個局部變量作用域的情況就比較復雜了,例如Grant Ri在其例子中給出的代碼


以下為引用:

delegate void NoArgs();

void SomeMethod()
{
NoArgs [] methods = new NoArgs[10];
int outer = 0;
for (int i = 0; i < 10; i++)
{
int inner = i;
methods[i] = delegate {
Console.WriteLine("outer = {0}", outer++);
Console.WriteLine("i = {0}", i);
Console.WriteLine("inner = {0}", ++inner);
};
methods[i]();
}
for (int j = 0; j < methods.Length; j++)
methods[j]();
}




就需要一個類封裝變量outer;一個類封裝變量i;另外一個類封裝inner和匿名函數,并引用前面兩個封裝類的實例。因為變量outer、i和inner有著不同的作用域,呵呵。偽代碼如下:


以下為引用:

private sealed class __LocalsDisplayClass$00000008
{
public int outer;

};
private sealed class __LocalsDisplayClass$0000000a
{
public int i;

};
private sealed class __LocalsDisplayClass$0000000c
{
public int inner;

public __LocalsDisplayClass$00000008 $locals$00000009;
public __LocalsDisplayClass$0000000a $locals$0000000b;

public void __AnonymousMethod$00000007()
{
Console.WriteLine("outer = {0}", this.$locals$00000009.outer++);
Console.WriteLine("i = {0}", this.$locals$0000000b.i);
Console.WriteLine("inner = {0}", ++this.inner);
}
};

public void SomeMethod()
{
NoArgs [] methods = new NoArgs[10];

__LocalsDisplayClass$00000008 local1 = new __LocalsDisplayClass$00000008();
local1.outer = 0;

__LocalsDisplayClass$0000000a local2 = new __LocalsDisplayClass$0000000a();
local2.i = 0;

while(local2.i < 10)
{
__LocalsDisplayClass$0000000c local3 = new __LocalsDisplayClass$0000000c();
local3.$locals$00000009 = local1;
local3.$locals$0000000b = local2;
local3.inner = local1.i;

methods[local2.i] = new NoArgs(local3.__AnonymousMethod$00000007);
methods[local2.i]();
}

for (int j = 0; j < methods.Length; j++)
methods[j]();
}





總結其規律就是每個不同的局部變量作用域會有一個單獨的類進行封裝,子作用域中如果使用到父作用域的局部變量,則子作用域的封裝類引用父作用域的封裝類。相同作用域的變量和匿名方法由封裝類綁定到一起,維護其一致的生命周期。

相對于MS較為復雜的實現,Delphi.NET對嵌套函數則使用較為簡單的參數傳遞方式,因為嵌套函數沒有那么復雜的變量生命期管理要求,如


以下為引用:

procedure SayHello;
var
Name: string;

procedure Say;
begin
WriteLn(Name);
end;
begin
Name := 'Flier Lu';

Say;
end;




系統生成函數Say代碼時,將使用到的上級變量如Name放入到一個自動生成的類型($Unnamed1)中,然后作為函數參數傳遞給Say函數,偽代碼類似


以下為引用:

type
$Unnamed1 = record
Name: string;
end;

procedure @1$SayHello$Say(var UnnamedParam: $Unnamed1);
begin
WriteLn(UnnamedParam.Name);
end;

procedure SayHello;
var
Name: string;
Unnamed1: $Unnamed1;
begin
Name := 'Flier Lu';

Unnamed1.Name := Name;

Say(Unnamed1);
end;

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統下載排行

網站地圖xml | 網站地圖html
主站蜘蛛池模板: 赤峰市| 铁岭市| 襄樊市| 靖西县| 无极县| 乌兰县| 高雄市| 南澳县| 广饶县| 盐城市| 中牟县| 东丰县| 庆阳市| 枞阳县| 屏南县| 凤翔县| 内黄县| 紫金县| 寻甸| 双桥区| 板桥市| 竹溪县| 九寨沟县| 故城县| 景宁| 阿坝县| 滁州市| 调兵山市| 合水县| 谢通门县| 榆树市| 诸城市| 青神县| 西青区| 海淀区| 什邡市| 吉安市| 府谷县| 大丰市| 阳春市| 淮北市|