在“有一定興趣”列表上的下一條提議是相當有爭議的,這條關於 Lambda 捕獲列表的提議假設,它能夠對閉包中的變量捕獲提供更多的控制能力。
這條提議一上來就講到了捕獲列表,這是一種常見於 C++ 中的概念。以下的示例中包含了一個常見的閉包,以及一個用捕獲列表所描述的閉包:
var x = 100; Func<int> a = () => x * 2; Func<int> b = [x] () => x * 2;
一旦使用這種語法,那麼任何一個沒有出現在捕獲列表(由中括號[x]表示)中的變量都無法在匿名方法中使用,否則將會產生一個編譯錯誤。如果你在這裡使用了一個空的列表[],那麼將不會創建任何閉包。這種做法對性能來說有好處,因為不產生閉包的匿名函數將無需進行內存分配。
如果要在閉包中訪問當前對象,需要通過使用[this]關鍵字,這種方式也能夠減少在無意中捕獲當前對象的可能性,因為這會導致內存的洩漏。
按值捕獲
有些情況下,你在閉包中只需要使用某個值的拷貝,而並不想讓它與原始值共享同一個變量。在這條提議中, 你可以通過以下方式使用捕獲列表來表現這一行為。
Func<int> c = [int xCopy = x]() => xCopy * 2;
這種語法非常冗長,因此在提議中也提出了以下幾種替代方式,它們的含義是完全相同的。
Func<int> d = [value x]() => x * 2; //this x is a copy Func<int> e = [val x]() => x * 2; //this x is a copy Func<int> f = [let x]() => x * 2; //this x is a copy Func<int> g = [=x]() => x * 2; //this x is a copy
提議中還建議使用以下語法,讓常見的按引用捕獲的閉包更為明確:
Func<int> h = [ref x]() => x * 2; //x is an alias (別名) Func<int> i = [&x]() => x * 2; //x is an alias
與之相關的一個提議是使用“細箭頭”(使用單橫線代替等號),它將隱式地按值捕獲所有變量。
Func<int> j = () -> x * 2; //this x is a copy
弱引用捕獲
正如之前所述,由於閉包的生命周期比創建它的函數更長,因此它是一種造成內存洩漏的常見原因。因此 Miguel do Icaza 建議在這條提議中加入弱引用的使用,Stephen Toub 對此提出了以下語法:
Action k = [weak myObject] () => […] Action l = [weak this] () => […] Action m = [wro = new WeakReference (myObject)] () => […]
批評意見
正如在介紹中所說,這條提議是富有爭議的。無論你選擇了哪種變種形式,新的語法都會讓代碼顯得相當雜亂。而且對於簡短的閉包來說,語法中所包含的信息很可能你早就從代碼本身看出來了。
為了支持向後兼容,捕獲列表的使用必須是可選的。而由於它的語法實在是非常冗長乏味,所以大多數開發者很可能不願意使用它,那麼這個特性存在的意義就令人懷疑了。
英文原文:C# Futures: Closure Annotations