丑:
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);
答案 0 :(得分:3)
是的,它应该是可能的。但是,它比在表面上看起来要复杂得多,以正确实现表达式树重写。特别是如果您希望能够正确处理在任意表达式中有效的字段,属性,索引属性,方法调用和其他构造。
它也可能不是性能最佳的操作,因为要评估表达式,每次都要将表达式树动态编译为lambda函数。
有一个 implementation on this pattern on CodePlex 。我从来没有亲自使用它,所以我不能说它是如何实现的,或者它是否处理我所描述的所有情况。
创建表达式树重写器的另一种方法是编写Maybe()
来接受lambda函数(而不是表达式树)并捕获任何抛出的ArgumentNullException
,返回default(T)
在那些情况下。它以许多人以错误的方式使用异常来以这种方式使用流量控制...但它肯定是一个更容易实现的实现。我个人自己避免使用它,因为它可以屏蔽作为表达式一部分的方法中的空引用错误,这是不可取的。
答案 1 :(得分:2)
我最近在C#中实现了一些monad(包括一个受Bartosz Milewski文章启发的基本表达式树解析器)。
看看你是否感兴趣:https://github.com/htoma/monads/blob/master/expressionMonad/expressionMonad/Program.cs
答案 2 :(得分:1)
我想到了一些观点:
。解决方案适用于内存对象,但在EF遇到问题,因为这些静态调用无法转换为针对持久存储(即SQL DB)运行。这在很大程度上限制了应用范围。
我几乎总是想知道链是否产生了有效的结果。因此,无论如何我都会有一个条件块if(city == null)
。
除了“丑陋”之外的任何当前解决方案都涉及表达式。
因此,我的选择就像是
var property = ( () => myOrder.Customer.City );
city = HasValue(property) ? property.Invoke() : "unknown";
HasValue(Expression e)
递归遍历LINQ表达式树,直到它到达end(返回true)或遇到null-value属性(返回false)。实现应该很简单,使用MethodInfo Member
类的MemberExpression
来解析AST。也可以像Brian建议的那样以这种方式实现getter,但我更喜欢上面因为HasValue
总是返回bool
。进一步:
myOrder.HasValue(x => x.Customer.City)
进行,但会带来一些复杂情况。答案 3 :(得分:0)
如果对象的创建成本低并且您想避免空检查,则回答更简单:
myOrder.NewIfNull().Customer.NewIfNull().City;
这将返回null或您在City的构造函数或字段初始值设定项中设置的某个初始值。 NewIfNull不是内置的,但它真的很简单:
public static T NewIfNull<T>(this T input) where T:new()
{
return input ?? new T();
}
答案 4 :(得分:0)
我知道我的Maybe(根据CodeProject文章)的实现带来了成本,但我确定它与没有相比于获得{{1涉及那里。基本上你一直都在谈论反思。如果它是预先编译的,罗斯林风格,我不会介意,但我们还没有。
我认为我的实施优势超越了神话?运营商。使用诸如此类的链编写整个算法的能力意味着您可以注入自己的创作(例如Expression<T>
,If
等)并提供您自己的专用逻辑。
我意识到这比你在这里尝试做的更复杂,但它看起来并不像我们要在C#5中获得一个零合并点运算符。< / p>