日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

C#2.0匿名函数

發(fā)布時(shí)間:2023/11/27 生活经验 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#2.0匿名函数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
C# 2.0中提供了通過delegate實(shí)現(xiàn)匿名函數(shù)功能,能有效地減少用戶記代碼工作,例如


以下為引用:

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




可以被簡化為直接使用匿名函數(shù)構(gòu)造,如


以下為引用:

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




關(guān)于匿名函數(shù)的使用方法可以參考Jeffrey Richter的Working with Delegates Made Easier with C# 2.0一文。簡要說來就是C#編譯器自動(dòng)將匿名函數(shù)代碼轉(zhuǎn)移到一個(gè)自動(dòng)命名函數(shù)中,將原來需要用戶手工完成的工作自動(dòng)完成。例如構(gòu)造一個(gè)私有靜態(tài)函數(shù),如


以下為引用:

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




被編譯器自動(dòng)轉(zhuǎn)換為


以下為引用:

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

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




而這里自動(dòng)生成的函數(shù)是否為static,編譯器根據(jù)使用此函數(shù)的地方是否static決定。這也是為什么C# 2.0規(guī)范里面禁止使用goto, break和continue語句從一個(gè)匿名方法里跳出,或從外面跳入其中的原因,因?yàn)樗麄兇a雖然寫在一個(gè)作用域里面,但實(shí)際上實(shí)現(xiàn)上并不在一起。
更方便的是編譯器可以根據(jù)匿名函數(shù)使用的情況,自動(dòng)判斷函數(shù)參數(shù),無需用戶在定義時(shí)指定,如


以下為引用:

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




在不使用參數(shù)時(shí),完全等價(jià)于


以下為引用:

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





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

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

首先,匿名函數(shù)使用到的父函數(shù)中局部變量,無聊是引用類型還是值類型,都必須從棧變量轉(zhuǎn)換為堆變量,以便在其作用域外的匿名函數(shù)實(shí)現(xiàn)代碼可以訪問并控制生命周期。因?yàn)闂W兞康纳芷谂c其所有者函數(shù)是一致的,所有者函數(shù)退出后,其堆棧自動(dòng)恢復(fù)到調(diào)用函數(shù)前,也就無法完成變量生命周期與函數(shù)調(diào)用生命周期的解耦。
例如下面這個(gè)簡單的匿名函數(shù)中,使用了父函數(shù)的局部變量,雖然此匿名函數(shù)只在父函數(shù)里面使用,但C#編譯器還是使用獨(dú)立類對(duì)其使用到的變量進(jìn)行了包裝。


以下為引用:

delegate void Delegate1();

public void Method1()
{
int i=0;

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

d1();
}




自動(dòng)生成的包裝代碼類似如下


以下為引用:

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();
}




但對(duì)于有多個(gè)局部變量作用域的情況就比較復(fù)雜了,例如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]();
}




就需要一個(gè)類封裝變量outer;一個(gè)類封裝變量i;另外一個(gè)類封裝inner和匿名函數(shù),并引用前面兩個(gè)封裝類的實(shí)例。因?yàn)樽兞縪uter、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]();
}





總結(jié)其規(guī)律就是每個(gè)不同的局部變量作用域會(huì)有一個(gè)單獨(dú)的類進(jìn)行封裝,子作用域中如果使用到父作用域的局部變量,則子作用域的封裝類引用父作用域的封裝類。相同作用域的變量和匿名方法由封裝類綁定到一起,維護(hù)其一致的生命周期。

相對(duì)于MS較為復(fù)雜的實(shí)現(xiàn),Delphi.NET對(duì)嵌套函數(shù)則使用較為簡單的參數(shù)傳遞方式,因?yàn)榍短缀瘮?shù)沒有那么復(fù)雜的變量生命期管理要求,如


以下為引用:

procedure SayHello;
var
Name: string;

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

Say;
end;




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


以下為引用:

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;?
?

總結(jié)

以上是生活随笔為你收集整理的C#2.0匿名函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。