[小技巧]让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;
        }
    }
}

 

 
posted @ 2014-09-11 14:46  Caption  阅读(2262)  评论(1编辑  收藏  举报