2010-12-09 45 views
5

醜:也許monad使用表達式樹?

string city = null; 
if (myOrder != null && myOrder.Customer != null) 
    city = myOrder.Customer.City; 

更好(maybe monad):

var city = myOrder 
       .With(x => x.Customer) 
       .With(x => x.City) 

更妙?任何理由不能寫出來?

var city = Maybe(() => myOrder.Customer.City); 

回答

3

是的,應該是可以的。然而,它比表面看起來要複雜得多,實現了表達式樹的重寫。特別是如果您希望能夠正確處理在任意表達式中有效的字段,屬性,索引屬性,方法調用和其他結構。

它可能也不是表現最好的操作,因爲評估表達式必須每次將表達式樹動態編譯爲lambda函數。

有一個implementation on this pattern on CodePlex。我從來沒有親自使用它,所以我不能說它有多好的實施,或者它是否處理我描述的所有情況。

創建表達式樹重新寫入器的替代,是寫Maybe()接受lambda函數(而不是表達式樹),並捕獲拋出任何ArgumentNullException,在那些情況下返回default(T)。它以這種方式使許多人以錯誤的方式使用異常來控制流量控制......但它確實更容易實現。我個人自己避開它,因爲它可以在稱爲表達式的一部分的方法中掩蓋空引用錯誤,這是不可取的。

0

簡單的答案,如果對象是廉價的創建和你想避免無效檢查:

myOrder.NewIfNull().Customer.NewIfNull().City; 

這將返回null或您在構造函數或字段初始爲城市設置一些初始值。 NewIfNull不內置,但它是真正的輕鬆:

public static T NewIfNull<T>(this T input) where T:new() 
{ 
    return input ?? new T(); 
} 
+0

創建新的對象可以有意想不到的後果。 – Amy 2010-12-10 00:02:08

0

我知道我的執行也許(按CodeProject上的文章)攜帶有成本的,但我敢肯定,這沒什麼相比,獲得的想法在那裏涉及一個Expression<T>。基本上你一直在說反射。我不介意它是否是預編譯的,羅斯林風格,但我們還沒有。

我認爲我的實現的優勢超出了神話嗎?運營商。使用這樣的鏈來編寫整個算法的能力意味着您可以注入自己的創作(例如IfDo等)並提供您自己的專用邏輯。我知道這比你在這裏試圖做的更復雜,但它看起來並不像我們要在C#5中得到一個null-coalescing點運算符。

1

浮現在我的腦海幾點:

  • .Solutions做工精細的內存中的對象,而是碰上與EF麻煩,因爲這些靜態調用不能轉換爲針對持久性存儲運行(即SQL D B)。這在很大程度上限制了應用範圍。

  • 我幾乎總是想知道是否鏈產生沒有有效的結果。因此,在任何情況下,我都會有一個條件塊if(city == null)

  • 不是「醜」以外的任何當前的解決方案涉及表達。

因此,我的選擇會是這樣

var property = (() => myOrder.Customer.City); 
city = HasValue(property) ? property.Invoke() : "unknown"; 

HasValue(Expression e)通過LINQ表達式樹遞歸地走,直到它到達終點(返回true)或遇到空值的屬性(返回false)。實現應該很簡單,使用MemberExpression類的MethodInfo Member來解析AST。也可以按照Brian的建議這樣實施getter,但我更喜歡上面的更好,因爲HasValue總是返回bool。進一步:

  • 成員invokations也可以處理。
  • 評估可以作爲myOrder.HasValue(x => x.Customer.City)但這會帶來一些複雜性。