我同意您的观点,?? 运算符通常用处有限 - 当某些东西为 null 时,使用它提供一个后备值很有用,但在你想继续深入访问有时为空引用的属性或方法的情况下,它并不适用于防止 Null Reference Exception。
在我看来,比 ?? 更需要的是一个“null-dereference”运算符,它允许您链式连接长的属性和/或方法链,例如 a.b().c.d().e
,而无需针对每个中间步骤进行 null 测试。 Groovy 语言中有一个 Safe Navigation operator,非常方便。
幸运的是,C#团队意识到了这个功能差距。请参见此 connect.microsoft.com suggestion ,了解 C# 团队将向 C# 语言添加 null-dereference 运算符的建议。
我们收到了不少类似这个的功能请求。社区讨论中提到的"?."
版本最接近我们的心意,它允许您在每个“点”处测试null,并与现有的??运算符很好地组合:
a?.b?.c ?? d
意思是如果a、a.b或a.b.c中的任何一个为空,则使用d。
我们正在考虑将其用于未来的发布版本,但不会出现在C# 4.0中。
再次感谢,
Mads Torgersen,C#语言PM
如果您也想在C#中添加此功能,请在connect网站上的
建议中投票! :-)
我使用的一个解决方法是使用扩展方法,就像
这篇博客文章中描述的那样,允许像这样编写代码:
string s = h.MetaData
.NullSafe(meta => meta.GetExtendedName())
.NullSafe(s => s.Trim().ToLower())
这段代码返回h.MetaData.GetExtendedName().Trim().ToLower()
,如果h、h.MetaData或h.MetaData.GetExtendedName()为null,则返回null。我还扩展了此功能以检查null或空字符串,或null或空集合。以下是我用于定义这些扩展方法的代码:
public static class NullSafeExtensions
{
public static TResult NullSafe<TCheck, TResult>(this TCheck check, Func<TCheck, TResult> valueIfNotNull)
where TResult : class
where TCheck : class
{
return check == null ? null : valueIfNotNull(check);
}
public static TResult CheckNullOrEmpty<TResult>(this string check, Func<string, TResult> valueIfNotNullOrEmpty)
where TResult : class
{
return string.IsNullOrEmpty(check) ? null : valueIfNotNullOrEmpty(check);
}
public static TResult CheckNullOrEmpty<TCheck, TResult>(this TCheck check, Func<TCheck, TResult> valueIfNotNullOrEmpty)
where TCheck : ICollection
where TResult : class
{
return (check == null || check.Count == 0) ? null : valueIfNotNullOrEmpty(check);
}
}