我们假设我已经定义了这些虚拟类:
public class MyObject
{
public InterestingFact InterestingFact { get; set; }
}
public class InterestingFact
{
public Detail Detail { get; set; }
}
public class Detail
{
public string Information { get; set; }
}
我还有MyObject
的实例:
var obj = new MyObject() { InterestingFact = new InterestingFact() };
如果我想尝试访问Information
属性,我可以这样做:
string info = null;
if (obj != null
&& obj.InterestingFact != null
&& obj.InterestingFact.Detail != null)
{
info = obj.InterestingFact.Detail.Information;
}
为了这次讨论,忽略了得墨忒耳定律,每次我想要访问我拥有的对象中的某些内容时,这并不是很有趣。当然,我可以创建一个扩展方法:
public static U NullOr<T, U>(this T target, Func<T, U> selector)
{
if (EqualityComparer<T>.Default.Equals(target, default(T)))
{
return default(U);
}
return selector(target);
}
这样可以更轻松地选择数据:
// Here I have a null of type string.
var info = obj.NullOr(o => o.InterestingFact).NullOr(f => f.Detail).NullOr(d => d.Information);
然而,这仍然有点长篇大论。理想情况下,我想做更像这样的事情:
// Here I should still have a null of type string.
var info = obj.NullOr(o => o.InterestingFact.Detail.Information);
我从未使用Expression
s;如果我在Expression<Func<T, U>>
中使用Func<T, U>
代替NullOr
,我认为它是一个成员访问而不是三个。有没有办法解决上述问题,或者这种配方是否遥不可及?
(当然,还有一个问题,即上次发送的表达式可能不仅仅是链式成员访问...)
答案 0 :(得分:0)
关于使用扩展方法获得所需结果的最简单方法如下。
public static class ExtensionMethods
{
public static TR DefaultIfNull<T, TR>(this T source, Expression<Func<T, TR>> expr, TR defaultValue = default(TR))
{
TR result = defaultValue;
try
{
result = expr.Compile().Invoke(source);
}
catch (NullReferenceException)
{
// DO NOTHING
}
return result;
}
}
以下是上述
的一些示例用法var info1 = obj.DefaultIfNull(x => x.InterestingFact.ToString(), "Null1");
var info2 = obj.DefaultIfNull(x => x.InterestingFact.Detail.ToString(), "Null2");
var info3 = obj.DefaultIfNull(x => x.InterestingFact.Detail.Information);
请注意,这不是 BEST 解决方案,因为它不会检查单个表达式节点的null,因此如果树中的任何节点恰好在内部生成NullReferenceException
,则默认将返回value而不是抛出异常。但是,对于一般和简单的用法,这可能是最佳解决方案(主要是因为它的简单性)。