寫 ASP.NET MVC CSHTML 時,我很習慣用 ViewBag 將變數從 Controller 傳到 View 端,只是簡單傳遞幾個字串、數值,為此大費周章宣告 Model 型別有點殺雞用牛刀。我們都知道 ViewBag 是一個 dynamic 型別,而 dynamic 型別的屬性、方法也會被視為 dynamic,編譯階段不檢查,執行階段見真章。

不過,最近學到一件事:一旦函式參數傳入 dynamic,其傳回值也會被視為 dynamic,而此時將無法使用 Lambda 運算式

來看下面這個例子。我計算透過 ViewBag.DateString 傳遞 "2017/08/26"格式字串 View,在 CSHTML 裡我用變數 dateStr 接入此字串,試著查 Length,跑 Split('/').First() 都沒問題。再來我寫了一個簡單函式 - MySplit,輸入 string,傳回 Split('/') 後的 string[]。將 dateStr 當參數傳入 MySpit,取傳回 string[] 的 Length OK,但想對 string[] 做 Any(o => o.Length > 3) 卻產生錯誤:

Error  CS1977  Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type. 無法將 Lambda 運算式當做動態分派作業的引數,而未先將它轉型為委派或運算式樹狀架構型別

依照錯誤訊息指示,(string[])MySplit(dateStr) 將其強轉型後問題即告排除。至此我才發現,原來不只 dynamic 的屬性會被視為 dynamic,連一般的函式方法,只要傳入 dynamic 傳回結果也會被視為 dynamic。這應該跟參數有 dynamic 時 .NET 會改用複雜機制動態觸發函式有關,詳情可參考前文:方法多載(Method Overloading)與 dynamic

我們用 DateTime.ParseExact 測試,傳入 dateStr,在傳回的 DateTime 上寫 DarkthreadYear 都可以編輯成功(當然,執行階段必爆無夷),而由 Visual Studio 的顯示也可確認它被視為 dynamic。

而在這個案例中,更簡單的解法是將 var dateStr 改成 string dateStr,多打兩個字元,問題消失殆盡!

了解到這個特性,我決定養成一個習慣,不再用 var 宣告變數承接 ViewBag 傳遞過來的資料,而要明確宣告變數型別。如此在編輯階段可全程享受 Visual Studio 的強型別檢查與 Intellisense 支援,也避免衍生 Lambda 運算式碰壁的困擾,而依據先前研究,參數為 dynamic 時,.NET 將改用較複雜的動態機制處理函式呼叫,使用強型別也將有助於提高效能,一舉多得,何樂不為?


Comments

# by edison

看来编码还是要规范咯

Post a comment