是否存在链式NULL检查?

时间:2014-04-22 22:17:32

标签: c# .net

我有以下丑陋的代码:

if (msg == null || 
    msg.Content == null || 
    msg.Content.AccountMarketMessage == null || 
    msg.Content.AccountMarketMessage.Account == null ||
    msg.Content.AccountMarketMessage.Account.sObject == null) return;

有没有办法在C#中检查空值,以便我不必检查每个单独的级别?

7 个答案:

答案 0 :(得分:9)

其中一个proposals in C# 6是添加新的Null Propogation operator

这将(希望)允许你写:

var obj = msg?.Content?.AccountMarketMessage?.Account?.sObject;
if (obj == null) return;

不幸的是,此时语言中没有任何内容可以解决这个问题。

答案 1 :(得分:4)

目前还没有这样的东西,但它可能很快就会进入.NET。有a well-known User Voice thread on the subject。正如this article中所述,Visual Studio团队最近宣布:

  

我们正在认真考虑C#和VB的这个功能,并且会   在未来几个月内对其进行原型设计。

编辑:并且如Reed Copsey's answer above中所述,它现在是C#6的计划添加。有关他链接的Codeplex页面的详细信息。

答案 2 :(得分:2)

没有内置支持,但您可以使用扩展方法:

public static bool IsNull<T>(this T source, string path)
{
     var props = path.Split('.');
     var type = source.GetType();

     var currentObject = type.GetProperty(props[0]).GetValue(source);

     if (currentObject == null) return true;
     foreach (var prop in props.Skip(1))
     {
          currentObject = currentObject.GetType()
                .GetProperty(prop)
                .GetValue(currentObject);

         if (currentObject == null) return true;
     }

     return false;
}

然后叫它:

if ( !msg.IsNull("Content.AccountMarketMessage.Account.sObject") )  return;

答案 3 :(得分:2)

你需要monad和Monadic null检查。可以查看Monads.Net package。它可以帮助简化空测试并从深度导航属性中获取值

这样的东西
var sObject = person.With(p=>p.Content).With(w=>w.AccountMarketMessage ).With(p=>p.Account).With(p=>p.Object);

如果您想要默认值,那么

var sObject = person.With(p=>p.Content).With(w=>w.AccountMarketMessage).With(p=>p.Account).Return(p=>p.Object, "default value");

答案 4 :(得分:2)

您可以使用lambda表达式懒惰地评估值。这对于简单的空值检查来说是过度的,但对于将更复杂的表达式链接在一个流畅的#34;方式。

实施例

// a type that has many descendents
var nested = new Nested();

// setup an evaluation chain
var isNull =
    NullCheck.Check( () => nested )
        .ThenCheck( () => nested.Child )
        .ThenCheck( () => nested.Child.Child )
        .ThenCheck( () => nested.Child.Child.Child )
        .ThenCheck( () => nested.Child.Child.Child.Child );

// handle the results
Console.WriteLine( isNull.IsNull ? "null" : "not null" );

代码

这是一个完整的示例(尽管是草稿质量代码),可以粘贴到控制台应用程序或LINQPad中。

public class Nested
{
  public Nested Child
  {
      get;
      set;
  }
}

public class NullCheck
{
   public bool IsNull { get; private set; }

   // continues the chain
   public NullCheck ThenCheck( Func<object> test )
   {
       if( !IsNull )
       {
           // only evaluate if the last state was "not null"
           this.IsNull = test() == null;
       }

       return this;
   }

   // starts the chain (convenience method to avoid explicit instantiation)
   public static NullCheck Check( Func<object> test )
   {
       return new NullCheck { IsNull = test() == null };
   }
}

private void Main()
{
   // test 1
   var nested = new Nested();
   var isNull =
       NullCheck.Check( () => nested )
           .ThenCheck( () => nested.Child )
           .ThenCheck( () => nested.Child.Child )
           .ThenCheck( () => nested.Child.Child.Child )
           .ThenCheck( () => nested.Child.Child.Child.Child );

   Console.WriteLine( isNull.IsNull ? "null" : "not null" );

   // test 2
   nested = new Nested { Child = new Nested() };
   isNull = NullCheck.Check( () => nested ).ThenCheck( () => nested.Child );

   Console.WriteLine( isNull.IsNull ? "null" : "not null" );

   // test 3
   nested = new Nested { Child = new Nested() };
   isNull = NullCheck.Check( () => nested ).ThenCheck( () => nested.Child ).ThenCheck( () => nested.Child.Child );

   Console.WriteLine( isNull.IsNull ? "null" : "not null" );
}

再次:由于其引入的复杂性,你可能不应该使用它代替简单的空检查,但它是一个有趣的模式。

答案 5 :(得分:0)

<强> .NET Fiddle

如前所述,有一个计划让c#6.0实现?运算符以便在某种程度上促进此过程。如果你不能等待,我建议使用lambda表达式和一个简单的辅助函数来解决这个问题。

public E NestedProperty<T,E>(T Parent, Func<T,E> Path, E IfNullOrEmpty = default(E))
{
    try
    {
        return Path(Parent);
    }
    catch
    {
        return IfNullOrEmpty;
    }
}

可以使用int value = NestedProperty<First,int>(blank,f => f.Second.Third.id);,如下面的演示所示

程序

public class Program
{
    public void Main()
    {
        First blank = new First();
        First populated = new First(true);

        //where a value exists
        int value = NestedProperty<First,int>(blank,f => f.Second.Third.id);
        Console.WriteLine(value);//0

        //where no value exists
        value = NestedProperty<First,int>(populated,f => f.Second.Third.id);
        Console.WriteLine(value);//1

        //where no value exists and a default was used
        value = NestedProperty<First,int>(blank,f => f.Second.Third.id,-1);
        Console.WriteLine(value);//-1
    }

    public E NestedProperty<T,E>(T Parent, Func<T,E> Path, E IfNullOrEmpty = default(E))
    {
        try
        {
            return Path(Parent);
        }
        catch
        {
            return IfNullOrEmpty;
        }
    }
}

简单的演示结构

public class First
{
    public Second Second { get; set; }
    public int id { get; set; }
    public First(){}
    public First(bool init)
    {
        this.id = 1;
        this.Second = new Second();
    }
}

public class Second
{
    public Third Third { get; set; }
    public int id { get; set; }
    public Second()
    {
        this.id = 1;
        this.Third = new Third();
    }
}

public class Third
{
    public int id { get; set; }
    public Third()
    {
        this.id = 1;
    }
}

答案 6 :(得分:0)

从3.5(可能更早)开始,您可以编写非常简单的扩展方法

  public static TResult DefaultOrValue<T, TResult> (this T source, 
                                                Func<T, TResult> property) where T : class
    {
        return source == null ? default(TResult) : property(source);
    }

您可以将此方法命名为更短,然后像这样使用

 var instance = new First {SecondInstance = new Second 
                          {ThirdInstance = new Third {Value = 5}}};
        var val =
            instance .DefaultOrValue(x => x.SecondInstance)
                .DefaultOrValue(x => x.ThirdInstance)
                .DefaultOrValue(x => x.Value);
        Console.WriteLine(val);
        Console.ReadLine();

所以源类是:

public class Third
{
    public int Value;
}

public class First
{
    public Second SecondInstance;
}

public class Second
{
    public Third ThirdInstance;
}