本做法提到了 Declarative code
與 Imperative code
兩種程式碼風格,還有提前與延遲執行推薦的使用情況。
採用 Imperative code
的寫法,內部的會根據你的命令先將資料準備好後再進行運算,例如下面這段程式碼需要傳入三個參數,
在命令式的風格下需要由上到下把 Method1
、Method2
、Method3
的值先求出來後才會調用 DoStuff()
方法。
var answer = DoStuff(Method1(),
Method2(),
Method3());
另一種寫法可以採用 lambda 或 query expression 來實現,雖然看起來與上面的例子差不多,但是實際上第一步驟是調用 DoStuff()
方法,
並把 Method1
、Method2
、Method3
傳給它,等待真正要用到這些方法的內容時才會運行該方法。
var answer = DoStuff(() => Method1(),
() => Method2(),
() => Method3())
那麼這兩種寫法有其中一個是比較好的嗎?其實還是要看實際需求,關鍵還是要修改後的結果會不會產生副作用,例如在做法 37 提到的時間案例, 變數 sequence 保存的並不是運算過後的值而是程式的邏輯,這也代表每次運作的時候結果都會不同,如果這個變化會影響你的程式那可能就不適合你。
有的時候把兩種方式混用會得到更好的效果,例如下面把 Method1()
委派賦值給 cache 變數,並且在 DoStuff 執行的時候會馬上運行 cache 變數
的委派並傳回值,這種方式可以盡快把需要的值緩存起來。
var cache = Method1();
var answer = DoStuff(() => cache,
() => Method2(),
() => Method3());
另外還有一個判斷標準是你這個方法需不需要在資料庫中執行也就是 LINQ to SQL,有些方法可以交給資料庫引擎執行,另一些就只能先當成本地方法處理,
之後 LINQ to SQL 會解析 expression tree
,最後再提交給資料庫前把那些需要本地方法處理的值替換上去。
Summary
這個做法主要在解析提前與延遲執行的運作方式與如何挑選,首先還是要確保兩種執行方式都不會影響呼叫方的結果,要先確定結果相同才來考慮到底哪種比較好, 如果在很判斷的場合可以先試著傳遞方法本身就好,因為它平常就是延遲執行也可以馬上變成提前執行非常靈活。