今天老趙在園子裡發了一篇文章"警惕匿名方法造成的變數共享",立即引起了大家的廣泛關注(老趙就是園子的"人氣天王",呵呵),而且這個問題園子裡也有其它幾篇文章做了研究
比如"閉包","《你不常用的c#之三》:action 之怪狀"
如果只是停留在c#2.0/3.0的"簡捷且優雅"的**風格上,初學者確實難理解這個"怪"現象,前二天買了本anytao的「你必須知道的.net」,裡面提供了一種研究這類表面"怪"現象的基本方法--il分析,並推薦了大名鼎鼎的反編譯工具"reflector",下面利用這個工具對其分析一二(高手就不必看了,權當給初學者一些參考)
原始**一(摘自"《你不常用的c#之三》:action 之怪狀"一文):
**1using
system;
using
system.collections.generic;
namespace
consoletest
foreach
(action action
inls)
system.console.read();
}
} }
結果:一連輸出了10行完全相同的"10"(可能並沒有按**編寫者的"意圖",輸出0到9),why?
開啟relector,先做一些設定:開啟"view"選單-->選擇"options",先去掉show pdb symbols前的勾,然後把optimization後的下拉框改為".net 1.0"(眾多的"語法糖",比如匿名方法,擴充套件方法等都是在1.0版本以後出現的,這樣設定的目的是去掉這些華麗的外衣,直接反應出原始的c#**),剛才的**經過反編譯後,大概如下:
[compilergenerated]
private
sealed
class
<>
c__displayclass2
}private
static
void
main(
string
args)
list.add(item);
class2.i++;}
foreach
(action action2
inlist)
console.read();
}可以看出,
1.編譯器自動生成了乙個密封類:<>c__displayclass2,裡面有乙個公有欄位i,以及乙個公共方法b__0()--用來輸出i
2.再看main方法中的高亮部分,自始至終,<>c__displayclass2就只生成了乙個例項class2,至於下面的while裡變來變去,也只不過在改變i這個變數(也就是例項class2的成員i),而我們知道「類(class)」是引用型別,實際上class2不過是個引用而已,所以每次用new action(class2.b__0)生成item,再list.add(item)進去後,每個item呼叫的都是同乙個引用,因此最終一連輸出10行相同的結果--即數字10,也就是理所當然了
把**1,稍作修改,如下:
using
system;
using
system.collections.generic;
namespace
consoletest
foreach
(action action
inls)
system.console.read();
}
} }
即在迴圈內部用乙個臨時變數lp做了乙個中轉,這次執行的結果,螢幕上輸出了0-9共10行不相同的結果
why?
還是用reflector來看看到底最終的**是啥?
[compilergenerated]
private
sealed
class
<>
c__displayclass1
}private
static
void
main(
string
args)
foreach
(action action
inlist)
console.read();
} 同樣,編譯器還是自動為我們生成了乙個密封類,這一點跟**1反編譯後的一樣,關注一下高亮部分,這回<>c__displayclass1 class2 = new <>c__displayclass1();是放在迴圈裡寫的,也就是說10次外迴圈走下來,一共建立了10個不同的c__displayclass1()例項,剩下的就不用多說了,看明白了吧
關於對於這個現象,個人覺得老趙的建議很好:委託建立完後,即時使用--no problem!(其實**1也可以改成這樣)
**1修改後
using
system;
using
system.collections.generic;
namespace
consoletest
system.console.read();
}
} }
結果正常,輸出0到9,再一次驗證了"立即使用"是沒問題的,但如果不是立即使用,就得多想想了
最後,其實本文所說的現象老趙在文中已經講得很明白了,我在這裡只不過向初學者推薦了一下反編譯的基本分析方法(當然你如果懂il的話,可以分析得更透),很多情況下,光看程式表面的現象,是很難想明白的,利用一些工具,找到表象下的本質相對更容易把握。
使用初始化捕獲來把物件移動到閉包
但那是c 11,c 14就不一樣啦,它直接支援將物件移動到閉包。如果你的編譯器支援c 14,歡呼吧,然後繼續讀下去。如果你依然使用c 11的編譯器,你還是應該歡呼和繼續讀下去,因為c 11有接近移動捕獲行為的辦法。缺少移動捕獲被認為是c 11的乙個缺陷,最直接的補救方法是在c 14中加上它,但標準委...
python 利用閉包模擬 c 靜態區域性變數效果
c 語言中靜態區域性變數只有在第一次被使用時初始化一次.示例如下 include int next num int main 執行結果 123 45 def instantiate func return func instantiate def next num a 0 def inner non...
利用閉包判斷Dom元素和滾動條的方向
一,判斷滾動條的方向,利用閉包首先儲存滾動條的位置,然後當滾動時候不斷更新滾動初始值,然後通過差指判斷方向 1 function scroll fn 5 window.addeventlistener scroll function false 12 13 scroll function direc...