C#中嵌套空值检查的简写方式

20

据我所知,没有一种显著更优雅的方法来编写以下内容...

string src;
if((ParentContent!= null)
    &&(ParentContent.Image("thumbnail") != null)
    &&(ParentContent.Image("thumbnail").Property("src") != null))
    src = ParentContent.Image("thumbnail").Property("src").Value

你认为是否应该有一个C#语言功能来使它更短?
如果是这样,它应该是什么样子?
例如,像扩展 ?? 运算符一样的东西。

string src = ParentContent??.Image("thumbnail")??.Property("src")??.Value;

非常抱歉这个例子有些牵强,我的解决方案过于简单。

编辑...多年后
现在这已是计划中的语言特性了,叫做"空传递运算符" ?. https://roslyn.codeplex.com/discussions/540883(感谢@Brian)


绝对迎合了我的甜食爱好! - David Perlman
1
请参阅 https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status&referringTitle=Documentation 和 https://roslyn.codeplex.com/discussions/540883。该功能应很快可用。 - Brian
它即将在C# 6中推出! - Philip Pittle
5个回答

18

没有内置的语法来实现这个功能,但是您可以定义一个扩展方法来实现:

R NotNull<T, R>(this T src, Func<T, R> f) 
    where T : class where R : class {
  return src != null ? f(src) : null;
}

现在,您可以将示例重写如下:
src = ParentContent.NotNull(p => p.Image("thumbnail")).
        NotNull(i => i.Property("src")).NotNull(src => src.Value);

虽然它没有语法支持那么好看,但我认为它更易读。

请注意,这会向所有 .NET 类型添加 NotNull 方法,这可能有点不方便。可以通过定义一个简单的包装类型 WrapNull<T> where T : class 来解决这个问题,该类型仅包含类型 T 的值以及将任何引用类型转换为 WrapNull 并在 WrapNull 类型中提供 NotNull 的方法。然后代码会像这样:

src = WrapNull.Wrap(ParentContent).NotNull(p => p.Image("thumbnail")).
        NotNull(i => i.Property("src")).NotNull(src => src.Value);

所以你不会用新的扩展方法污染每个类型的 IntelliSense。

如果再花费一些努力,你还可以定义一个 LINQ 查询操作符来实现这一点。虽然有些过度设计,但如果有人感兴趣,它是可以实现的(我不会在此处包含定义,因为它们有点长 :-))。

src = from p in WrapNull.Wrap(ParentContent)
      from i in p.Image("thumbnail").
      from src in i.Property("src")
      select src.Value;

2
我必须说,我不得不读了大约5次才能完全理解它。有时候阅读函数式代码让我后悔最初没有选择LISP等语言而是选择了过程式编程风格。但有些日子,我感谢自己没有这样做!:D - Serapth
@Serapth:我认为,一件事情是阅读实现代码(并理解其工作原理),另一件事情是阅读用户代码。解释如何使用我的“NotNull”扩展方法不应该那么困难(也许我在这里做得不够好:-))。 - Tomas Petricek
哦,我完全同意。实际上,使用扩展方法是再简单不过的事情了,而且比原帖中推荐的解决方案更易懂。但是,对于我来说,扩展方法本身非常难以阅读。话虽如此,函数式编程并不是自然直观的,事实上,我始终觉得模板代码为场景增加了一层混乱。我想这只是我的大脑的工作方式。我理解语法、理解正在发生的事情以及它是如何工作的,只是需要多次阅读才能跟上它的运行流程。 - Serapth
唯一需要注意的是,它的行为方式并不完全与一堆嵌套的 if 语句或三元运算符相同;即使 lambda 表达式没有被执行,链中的所有包装器方法都将被执行。大多数情况下这可能不是问题,但需要记住的是,这不是一个短路求值器。 - Aaronaught
@Aaronaught:你可以嵌套它们:a.NotNull(b => b.Foo.NotNull(c => c.Bar)) - 然后它的行为就像嵌套的ifs一样。(我认为非嵌套使用在介绍中更易读) - Tomas Petricek
这是一个两难的选择,你的回答中有更多酷炫的东西,但Aaronaught的回答更直接回答了问题。 - Myster

16

有人提出并被团队拒绝的建议:

为null值提供更多C#语法糖

该提议的语法看起来像a.?b.?c() - 非常有用且明确。

我也很想看到它,但看起来不太可能发生。 也许如果有足够多的人投票支持它!


15

我们在考虑C# 4时曾经考虑过这个功能,但是我们没有预算。这是一个很多人要求的不错的功能,也许我们会在未来的某个假想语言版本中实现它。不承诺。


9
有一天,埃里克会度过漫长的一天,感到非常疲倦。在那一天,他会说:“我们正在考虑将其纳入下一个C#版本。” - Brian
@杰克:抱歉,我不再是考虑这件事的人了! - Eric Lippert
1
Nooooooooooooooooooooooooo - Jack

1

如果我错了,请纠正我,但是这可以使用C# 6.0的null-conditional operator来解决:

string src = ParentContent?.Image("thumbnail")?.Property("src")?.Value;

如果在此值分配之前已经使用了src,则可以按以下方式使用它:
string src = ....;

// ...

src = ParentContent?.Image("thumbnail")?.Property("src")?.Value ?? src;

// ...

0

它仍然有些不足,但我会这样写:

var src = ParentContent == null ? null
    : ParentContent.Image("thumbnail") == null ? null
    : ParentContent.Image("thumbnail").Property("src") == null ? null
    : ParentContent.Image("thumbnail").Property("src").Value;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接