C# 6中的空条件运算符和字符串插值

18

请问null-conditional operatorinterpolated strings语法只是 syntactic sugar 吗?

在C# 6中,null-conditional运算符(?.)允许通过减少“过多”的null检查来清理代码,并且插值字符串(("\{X}, \{Y}"))将参数和格式组合成一个字符串。

这些是否被编译为不可取的对应方法(即我们想要避免的丑陋代码)?

非常抱歉我提出了这种幼稚的问题,我的语言学习水平不是最好的,但我想知道在C#5上是否可以使用这些特性。

我知道Java的一些情况下也是如此,这些例子也是这样吗?


2
Elvis 操作符是另一回事。它实际上是一个空值合并运算符,它选择第一个非空值。 ?. 试图沿着属性链遍历对象,只有在整个链路求值成功时才返回非空值。http://en.wikipedia.org/wiki/Null_coalescing_operator UPDATE - 找到了 ?. 的名称,它被称为安全导航操作符 - Victor Zakharov
C#已经有??运算符了。那不是一样的吗? - Ryan Bemrose
似乎不是。@RyanBemrose。也许是,但已经改变了吗? - ChiefTwoPencils
4
给未来读者的更新:在C# 6.0中,“Elvis操作符”已正式更名为“null-conditional操作符”。 - Nick Orlando
6
我更喜欢使用Elvis运算符 :) - Randall Deetz
1
谁在乎你的代码编译成什么?你唯一需要考虑的是:1)以最简洁、明确的方式表达算法,2)编译结果有多快、可靠。你知道 IL 中经常使用 GOTO,对吗?GOTO。想象一下,所有优雅的干净代码都变成了一堆糟糕的 GOTO 语句。现在去冲个澡吧。 - user1228
3个回答

32

没有一个通用规则,因为情况各异。有些功能只是语法糖,有些增加了以前不可能实现的功能,而有些则是两者的组合。

语法糖

  • 字符串插值 - 这样写:

    string result = $"{bar}";
    

    改为:

    string result = string.Format("{0}", bar);
    
  • 空值传播运算符(?.) - 它的作用是:

    var result = Foo()?.Length
    

    改为:

    var temp = Foo();
    var result = (temp != null) ? temp.Length : null;
    

新功能

  • 字符串插值 - 添加了对IFormattable的支持,可以使用FormattedString实现以下操作:

IFormattable result =  $"{bar}"
  • catchfinally 块中,现在可以使用 await

    try
    {
    }
    catch
    {
        await Task.Delay(1000);
    }
    
  • 当然,这两个类别中还有更多的功能,例如异常过滤器和表达式成员。


    那么,程序员的一般规则可能是要么彻底了解这些功能的行为,要么默认使用它们的实现版本。 - ChiefTwoPencils
    @ChiefTwoPencils 这很合理。 - i3arnon
    所以,显然我一直从基础层面开始编写C#。为什么需要使用?.,如果我们已经有了评论中建议的???它们是多余的还是被更改了? - ChiefTwoPencils
    2
    @ChiefTwoPencils,它们是不同的运算符,具有不同的目的。?? 是空值合并运算符。?. 是空值条件运算符(或安全导航运算符)。 - i3arnon
    4
    1. var result = GetString() ?? string.Empty; 变量result的值为GetString()方法的返回值,如果该方法返回null,则将其赋值为空字符串。
    2. var result = GetString()?.Length 变量result的值为GetString()方法的返回值的长度,如果该方法返回null,则result的值也为null。
    - i3arnon
    1
    @PauloMorgado 当然。这就是为什么它已经在IL中可用了。 - i3arnon

    11

    C#6.0中的大多数新功能一样,null-conditional操作符只是获取成员变量值的一种模式的快捷方式(如果你想这么称呼,可以称为语法糖),前提是使用的变量实例不为null。

    对于类型为string的s,以下代码:

    int? l = s?.Length;
    

    被翻译成:

    int? l = s == null ? null : s.Length;
    

    它可以与空值合并运算符 (??) 结合使用:

    int l = s?.Length ?? 0;
    

    字符串插值最初是 string.Format 的简写,但已经演化成一个模式,可以生成一个 string 或者一个 IFormatble。请参考当前规范获取更多信息。

    顺便说一下,roslyn 是编译器平台的代号,而不是语言或其特性。


    是的,Roslyn 是编译器平台的名称。我询问了这些功能所编译的代码,有人编辑了帖子并添加了标签,我认为这很合适/合理。对于您的声明和被接受的答案之间的区别/澄清将是很好的。感谢提供链接;我目前没有时间阅读规范,因此在这里提问,但很快会深入研究。 - ChiefTwoPencils
    1
    @ChiefTwoPencils,我发布的链接是有关字符串插值规范的 - 相当简短。至于“Elvis”运算符,除非你想花一个月或两个月去浏览所有讨论,否则它基本上就是我发布的内容。 - Paulo Morgado

    2

    当调用RaisePropertyChanged事件时,Elvis运算符非常有用。

    过去,您需要编写

    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(nameof(X));
    }
    

    但是,如果在调用之前将PropertyChanged设置为null,则可能存在潜在的多线程问题。相反,您可以编写:

    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(X));
    

    这完全避免了多线程问题-所以它不完全是语法糖。


    1
    小注:在过去,nameof()并不存在。它是与空值条件运算符一起进入语言的。此外,竞态条件是通过将两个引用提取到本地变量中来解决的。因此,你“只是”节省了一些按键次数(这并不一定是可以嘲笑的事情)。 - sara
    1
    更小的注释:空值传播运算符是 ?.,你上面提到的是 Elvis 运算符,它是 ?:,看起来确实像 Elvis。 - ericosg
    1
    不,它并没有避免多线程问题,只是将它们稍微移了一下。请参见https://dev59.com/r1sV5IYBdhLWcg3wyxZ_#35786442。 - Micha Wiedenmann
    @MichaWiedenmann,MSDN:https://msdn.microsoft.com/en-us/library/dn986595.aspx表明相反的情况。 - astrowalker

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