2010-12-09 42 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)但这会带来一些复杂性。