閉包捕獲與lazy val欄位
其中一位同事對Scala不熟而對微軟系技術更熟,於是我就把這個例子用C#來寫了一次給他看。結果他沒聽說過C#里有System.Lazy<T>…不過還好C#的Lazy<T>是標準庫層面的功能,畢竟magic少一些,還是比Scala的好解釋一些。
C#和Scala的代碼例子在這個gist里:demo C# and Scalas lazy val and lambda capturing
真正的重點是:
- C#和Scala里如果「捕獲了欄位」的話,其實捕獲的是this而不是單獨捕獲了一個欄位,在lambda里訪問欄位其實是通過捕獲的this來訪問的。Java 8的lambda也是如此。
- 對this的捕獲並不會導致lazy val被求值,所以就算lambda里會用到一個lazy val欄位,在閉包創建的時候並不會當時就導致該欄位求值。如果要包裝這個lazy val欄位在lambda表達式實際被調用時得到閉包創建時的一些環境狀態的話,得自己顯式在閉包創建前對這個lazy val欄位求一下值才行。
然後同事表示還是C++好,lambda的捕獲列表很明確哪些是capture-by-value哪些是capture-by-reference。然而其實來個 [&] 就可以把按引用捕獲this的事實給隱含起來了,對新手來說也沒友好多少吧(ry
#include <iostream>n#include <functional>nnclass Foo {n int val_ = 0;nnpublic:n std::function<int()> get_func() {n return [&]() { return val_; };n }nn void incr() { val_++; }n};nnint main() {n Foo foo;n const auto& f = foo.get_func();n std::cout << f() << std::endl;n foo.incr();n std::cout << f() << std::endl;n return 0;n}n
讓不熟悉現代C++的人來讀這段代碼,能看出來那個lambda隱式按引用捕獲了this么 >_<
推薦閱讀:
※在C#的ArrayList中的對象可以是結構體嗎?如果可以,怎麼使用比較簡便?補充:結構體包含多個欄位。
※Unity3D 5.3 新版AssetBundle使用方案及策略
※從遊戲腳本語言說起,剖析Mono搭建的腳本基礎
※Visual Studio 開發體驗究竟牛到什麼程度?真的只是拖拖控制項就能完成中小型項目開發?
※【譯】介紹 .NET Standard