有没有像链式NULL检查这样的东西
本文关键字:检查 NULL 有没有 | 更新日期: 2023-09-27 17:59:42
我有以下丑陋的代码:
if (msg == null ||
msg.Content == null ||
msg.Content.AccountMarketMessage == null ||
msg.Content.AccountMarketMessage.Account == null ||
msg.Content.AccountMarketMessage.Account.sObject == null) return;
有没有一种方法可以对C#中的null值进行链式检查,这样我就不必检查每个级别了?
C#中的一个建议是添加一个新的Null Propagation运算符。
这将(希望)允许你写:
var obj = msg?.Content?.AccountMarketMessage?.Account?.sObject;
if (obj == null) return;
不幸的是,在这一点上,语言中没有任何东西可以处理这个问题。
目前还没有这样的东西,但它可能很快就会出现在.NET中。关于这个主题,有一个著名的用户之声主题。正如本文所指出的,VisualStudio团队最近宣布:
我们正在认真考虑C#和VB的这一功能,并将在未来几个月内进行原型设计。
编辑:正如Reed Copsey在上面的回答中所指出的,它现在是C#的一个计划添加。他链接的Codeplex页面上有更好的细节。
没有内置的支持,但可以使用扩展方法:
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;
您需要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表达式延迟计算值。对于简单的null检查来说,这太过分了,但对于以"流畅"的方式链接更复杂的表达式来说,这可能很有用。
示例
// 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" );
}
再次:您可能不应该使用它来代替简单的null检查,因为它引入了复杂性,但这是一个有趣的模式。
.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;
}
}
由于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;
}