探索c 之函式建立和閉包

2022-01-17 12:18:49 字數 3177 閱讀 2155

動態建立函式

匿名函式不足之處

理解c#中的閉包

閉包的優點

大多數同學,都或多或少的使用過。回顧下c#中動態建立函式的進化:

c# 1.0中:

public

delegate

string dynamicfunction(string

name);

public

static

dynamicfunction getdynamicfunction()

static

string getname(string

name)

var result = getdynamicfunction()("

mushroom

");

3.0寫慣了是不是看起來很繁瑣、落後。 剛學委託時,都把委託理解成函式指標,也來看下用函式指標實現的:

char getname(char

p);typedef

char (*dynamicfunction)(char

p);dynamicfunction getdynamicfunction()

char getname(charp);

char result = getdynamicfunction()('

m');

對比起來和c# 1.0幾乎一模一樣了(引用/指標差別),畢竟是同一家族的。

c# 2.0中,增加匿名函式:

public

delegate

string dynamicfunction(string

name);

dynamicfunction result2 = delegate(string

name)

;

c# 3.0中,增加lambda表示式,華麗的轉身:

public

static funcgetdynamicfunction()

var result = getdynamicfunction()("

mushroom

");

雖然增加lambda表示式,已經極大簡化了我們的工作量。但確實有些不足之處:

var result = name => name;
這些寫編譯時是報錯的。因為c#本身強型別語言的,提供var語法糖只是為了省去宣告確定型別的工作量。 編譯器在編譯時必須能夠完全推斷出各引數的型別才行。**中的name引數型別,顯然在編譯時無法推斷出來的。

var result = (string name) =>name;

func

result2 = (string name) =>name;

expression

string, string>> result3 = (string name) => name;

上面直接宣告name型別呢,很遺憾這樣也是報錯的。**中已經給出答案了,編譯器推斷不出右邊表示式是屬於func型別還是expression>型別。

dynamic result = name =>name;

dynamic result1 = (func)(name => name);

用dynamic呢,同樣編譯器也分不出右邊是個委託,我們顯示轉換下就可以了。

func function = name =>name;

dynamicfunction df = function;

這裡定義個func委託,雖然引數和返回值型別都和dynamicfunction委託一樣,但編譯時還是會報錯:不能隱式轉換func到dynamicfunction,2個型別是不相容的。

談論到動態建立函式,都要牽扯到閉包。閉包這個概念資料很多了,理論部分這裡就不重複了。 來看看c#**中閉包:

funcint>> a = () =>;

};var result = a()();

上面就是閉包,可理解為就是:跨作用域訪問函式內變數,也有說帶著資料的行為。

c#變數作用域一共有三種,即:類變數,例項變數,函式內變數。子作用域訪問父作用域的變數(即函式內訪問例項/類變數)在我們看來理所當然的,也符合我們一直的程式設計習慣。

例子中匿名函式b是可以訪問上層函式a的變數age。對於編譯器而言,a函式是b函式的父作用域,所以b函式訪問父作用域的age變數是符合規範的。

int age = 16

;

void

display()

上面編譯會報錯未宣告使用,編譯器檢查到函式內宣告age後,作用域就會覆蓋父作用域的age,(像js就undefined了)。

func c = () =>;
上面宣告個同級函式c,那麼a函式是無法訪c函式中的age變數的。 簡單來說就是不可跨作用域訪問其他函式內的變數。 那編譯器是怎麼實現閉包機制的呢?

如上圖,答案是公升級作用域,把a函式公升級為乙個例項類作用域。 在編譯**期間,編譯器檢查到b函式使用a函式內變數時,會自動生成乙個匿名類x,把原a函式內變數age提公升為x類的字段(即例項變數),a函式提公升為匿名類x的例項函式。下面是編譯器生成的**(精簡過):

class

program1

static funcb()

}sealed

class

displayclass

}

view code

我們再來看個複雜點的例子:

static funcgetclosurefunction()

console.writeline(getclosurefunction()(

30));

輸出結果是20、40、60。 當看到這個函式內變數val通過閉包被傳遞的時候,我們就知道val不僅僅是個函式內變數了。之前我們分析過編譯器怎麼生成的**,知道val此時是乙個匿名類的例項變數,interadd是匿名類的例項函式。所以無論val傳遞多少層,它的值始終保持著,直到離開這個(鏈式)作用域。

關於閉包,在js當中談論的比較多,同理,可以對比理解下:

function

a()

}a()();

python之函式,閉包

引數 收集引數 引數名 def stu info print info 0 print info 1 print len info print type info stu shanxi 200008966 19 列印結果 shanxi 200008966 3 class tuple 返回值 def ...

python之閉包函式

def closure conf prefix def innerfunc name print prefix,name return innerfunc holiday closure conf 10月1日是 holiday 國慶節 print function name is holiday.n...

python函式閉包和遞迴 函式和閉包之尾遞迴

前面提到過,如果想把更新var的while迴圈轉換成僅使用val這種更函式式的風格的話,有時候你可以使用遞迴。下面的例子是通過不斷改善猜測數字來逼近乙個值的遞迴函式 var guess initialguess while isgoodenough guess guess improve guess...