是否存在链式NULL检查?

我有以下丑陋的代码:

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

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

C#6中的一个提议是添加一个新的Null Propogation运算符 。

这将(希望)允许你写:

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

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

目前还没有这样的东西,但它可能很快就会进入.NET。 该主题上有一个众所周知的用户语音线程 。 正如本文所述,Visual Studio团队最近宣布:

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

编辑:正如Reed Copsey上面的回答所述 ,它现在是C#6的计划添加。在他链接的Codeplex页面上有更好的细节。

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

 public static bool IsNull(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; 

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

就像是

 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"); 

您可以使用lambda表达式懒惰地评估值。 这对于简单的空检查来说是过度的,但对于以“流畅”的方式链接更复杂的表达式可能是有用的。

 // 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 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 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" ); } 

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

.NET Fiddle

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

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

这可以用于int value = NestedProperty(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(blank,f => f.Second.Third.id); Console.WriteLine(value);//0 //where no value exists value = NestedProperty(populated,f => f.Second.Third.id); Console.WriteLine(value);//1 //where no value exists and a default was used value = NestedProperty(blank,f => f.Second.Third.id,-1); Console.WriteLine(value);//-1 } public E NestedProperty(T Parent, Func 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; } } 

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

  public static TResult DefaultOrValue (this T source, Func 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; }