[小技巧]让C#的空值处理变得更优雅
参考
http://www.codeproject.com/Articles/739772/Dynamically-Check-Nested-Values-for-IsNull-Values?msg=4895299#xx4895299xx
http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/
介绍
C#中的空值处理,一直是一件比较让人不爽的工作。假设现在有如下类:
public class Union { public string Name { get; set; } } public class Dep { public Union Union { get; set; } public string Name { get; set; } } public class Person { public Dep Dep { get;set;} public string Name { get; set; } }
如果Person.Dep.Union.Name.Length>5,则写入日志,代码该怎么写呢?很可能是这样:
if (person != null && person.Dep != null && person.Dep.Union != null && person.Dep.Union.Name != null && person.Dep.Union.Name.Length > 5) { Console.WriteLine(person.Dep.Union.Name); }
逻辑语句写得多的各位,对于这样写的繁琐应该深有体会。
对策
1扩展方法在访问对象前,会先进入其对应的静态方法。可以很方便的对this参数进行判断和处理,而不引起nullreference异常;
2委托可以很好的进行扩展方法的后续动作。
我们可以这样写上面的语句
person
.GoTo(p => p.Dep.Union.Name)
.If(n => n.Length >= 5)
.Do(Console.WriteLine);
很简洁,是不是?怎么实现呢?只需加入下列扩展类:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace LinqTesting
{
public static class Helper
{
class IsNullVisitor : ExpressionVisitor
{
public bool IsNull { get; private set; }
public object CurrentObject { get; set; }
protected override Expression VisitMember(MemberExpression node)
{
//it will call this overrided method with higher level node
base.VisitMember(node);
if (CheckNull())
return node;
var member = (PropertyInfo)node.Member;
CurrentObject = member.GetValue(CurrentObject, null);
CheckNull();
return node;
}
private bool CheckNull()
{
if (CurrentObject == null)
IsNull = true;
return IsNull;
}
}
public static TReturn GoTo<T, TReturn>(this T root, Expression<Func<T, TReturn>> funcGetValue)
{
var visitor = new IsNullVisitor();
visitor.CurrentObject = root;
visitor.Visit(funcGetValue);
if (visitor.IsNull)
return default(TReturn);
return (TReturn)visitor.CurrentObject;
}
public static TInput If<TInput>(this TInput o, Func<TInput, bool> evaluator)
where TInput : class
{
if (o == null) return null;
return evaluator(o) ? o : null;
}
public static TInput Unless<TInput>(this TInput o, Func<TInput, bool> evaluator)
where TInput : class
{
if (o == null) return null;
return evaluator(o) ? null : o;
}
public static TInput Do<TInput>(this TInput o, Action<TInput> action)
where TInput : class
{
if (o == null) return null;
action(o);
return o;
}
}
}
浙公网安备 33010602011771号