最近在學習golang的時候發現一件有趣的事情,go有一個閉包的概念,因而我對比了一下C#的"閉包"...golang
calc 是一個接收一個形參a、兩個函數返回值的函數,兩個函數返回值是指這一段代碼:(func(int) int, func(int) int),go語言支持多個返回值
main 函數,全部語言同樣程序的入口
【1】 經過main函數調用calc函數執行返回兩個函數,分別是f1和f2,同時會打印一邊參數a的內存地址
【2】 調用f1函數和f2函數,打印一遍add和sub裏面"引用"calc函數參數a的內存地址,並返回calc參數a+=i和a-=i的結果
【3】 輸出f1函數和f2函數執行返回的結果 安全
1 func calc(a int) (func(int) int, func(int) int) { 2 //打印變量地址 3 fmt.Println(&a) 4 5 //建立a+=add函數入參i,並返回的a的結果的函數,並返回給調用者 6 add := func(i int) int { 7 //返回值函數內部打印一遍變量地址,測試這裏面的a和calc的a內存地址是否一致 8 fmt.Println(&a) 9 a += i 10 return a 11 } 12 //建立a-=sub函數入參i,並返回的a的結果的函數,並返回給調用者 13 sub := func(i int) int { 14 fmt.Println(&a) 15 a -= i 16 return a 17 } 18 //返回兩個函數 19 return add, sub 20 } 21 22 func main() { 23 //f1對應add f2對應sub 24 f1, f2 := calc(10) 25 //調用f1和f2函數,同時打印f1 f2返回結果 26 fmt.Println(f1(1), f2(2)) //11 9 27 fmt.Println(f1(3), f2(4)) //12 8 28 fmt.Println(f1(5), f2(6)) //13 7 29 }
打印結果:閉包
0xc00000a0a8 調用calc函數所打印 fmt.Println(&a) 的結果函數
0xc00000a0a8 調用f1(1) add裏面的 fmt.Println(&a)
0xc00000a0a8 調用f2(2) sub裏面的 fmt.Println(&a)
11 9 f1(1)和f2(2) 返回值學習
0xc00000a0a8 調用f1(3)
0xc00000a0a8 調用f1(4)
12 8 f1(3)和f2(4) 返回值測試
0xc00000a0a8 調用f1(5)
0xc00000a0a8 調用f1(6)
13 7 f1(5)和f2(6) 返回值this
是否是很驚訝,calc函數參數a這個值變量變成了引用關係,在calc函數執行完以後並無釋放,並供給add和sub使用,這就是go的閉包。spa
經過下面代碼能「實現」上面golang的閉包指針
1 public void Calc(int a, out Func<int, int> add, out Func<int, int> sub) 2 { 3 add = (int i) => 4 { 5 return a += i; 6 }; 7 8 sub = (int i) => 9 { 10 return a -= i; 11 }; 12 } 13 14 public void Show() 15 { 16 Func<int, int> f1; 17 Func<int, int> f2; 18 this.Calc(10, out f1, out f2); 19 20 Console.WriteLine(string.Format("{0} {1}", f1(1), f2(2))); 21 Console.WriteLine(string.Format("{0} {1}", f1(3), f2(4))); 22 Console.WriteLine(string.Format("{0} {1}", f1(5), f2(6))); 23 }
返回值打印結果:調試
11 9
12 8
13 7
得出結論確實是能實現和golang上面代碼同樣的功能,calc函數參數a變量變成"引用"關係,提供給add和sub使用。
可是...我將代碼改了下
1 public class TestFunc 2 { 3 //存儲內存地址的字典,供後續查看 4 private unsafe Dictionary<string, int*[]> PADic = new Dictionary<string, int*[]>(); 5 6 public void Calc(int a, out Func<int, int> add, out Func<int, int> sub) 7 { 8 //運行執行不安全的代碼 9 unsafe 10 { 11 //取a的內存地址 12 int* aa = (int*)a; 13 //將內存地址存儲到字典中 14 this.PADic.Add("Calc(a):", new int*[] { aa }); 15 } 16 17 add = (int i) => 18 { 19 unsafe 20 { 21 int* aa = (int*)a; 22 this.PADic.Add(string.Format("f1({0}):", i), new int*[] { aa }); 23 } 24 return a += i; 25 }; 26 27 sub = (int i) => 28 { 29 unsafe 30 { 31 int* aa = (int*)a; 32 this.PADic.Add(string.Format("f2({0}):", i), new int*[] { aa }); 33 } 34 return a -= i; 35 }; 36 } 37 38 public void Show() 39 { 40 Func<int, int> f1; 41 Func<int, int> f2; 42 this.Calc(10, out f1, out f2); 43 Console.WriteLine(string.Format("{0} {1}", f1(1), f2(2))); // 11 9 44 Console.WriteLine(string.Format("{0} {1}", f1(3), f2(4))); // 12 8 45 Console.WriteLine(string.Format("{0} {1}", f1(5), f2(6))); // 13 7 46 } 47 }
(指針地址沒辦法輸出,直接調試看變量內容)輸出結果:
經過上面代碼能夠看出,每次調用f1和f2的時候,所"引用"的參數a是「值複製」的,而不是引用,由於它們的內存地址是有變化。
得出結論,功能雖然都能實現,可是有本質區別的。