【问题标题】:the null-coalescing operator with return statement带有 return 语句的空合并运算符
【发布时间】:2019-09-16 15:56:05
【问题描述】:

正如您在C# documentation 中看到的那样,我们可以编写空合并运算符结合 throw 表达式,如下所示

public string Name
{
    get => name;
    set => name = value ?? 
        throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
}   

但在很多情况下,如果左值为空,我需要返回语句,例如下面的内容

public double CalculateSomthing(ClassType someInstance)
{
    var someValue = someInstance?.Value ?? return double.NaN;

    // Call some method
    AnyMethod(someValue);

    // Some Computation
    return someValue + 17;
}

但我知道我无法编写类似上面的代码,我的问题是为什么?为什么 C# 语言设计器允许使用 null 合并运算符编写 throw 表达式,但不允许使用带有合并运算符的 return 语句?

它们之间有什么本质区别吗?

在现有的 C# 版本中是否有类似上述示例的解决方案?或者我必须编写一些样板代码,如下所示

public double CalculateSomthing(ClassType someInstance)
{

    var someValue = someInstance?.Value;
    if (someValue == null) return double.NaN;  // Boiler plate

    // Call some method
    AnyMethod(someValue);

    // Some Computation
    return someValue + 17;
}

【问题讨论】:

  • 可以写这样的代码since C# 6。 throw 表达式是在 C# 7 中添加的。
  • @LGSon OP 要求return 表达式 而不是return 语句。就像throw statement 已从 C#6 更改为 C#7 一样,它可以用于编译器通常需要 expression 的地方。他们不想在第一个值为空的情况下继续该方法,而是只想返回而不是抛出。这在技术上是可行的,但显然没有人对此提出足够的要求,也没有人愿意实施、测试、记录和发布它。
  • someValue??double.NaN 有效。你尝试了什么,失败了什么?您确定问题不在于?? 之后的无效返回语句吗?
  • @PanagiotisKanavos 无效语句是 OP 所要求的:return 表达式(而不是语句),就像有一个 throw 表达式一样。他们想要返回右侧的值,以防左侧的值为空。
  • 您可以在 C# Language GitHub 页面上发布提案。如果它适用于冠军,也许您会在即将推出的语言版本中看到此功能。

标签: c# null-coalescing-operator c#-8.0


【解决方案1】:

Return/break/continue expressions 已经讨论过,但很难正确实现,因为它们会干扰其他构造。充其量,这是一个方便的功能。正如设计会议记录所说:

这是一个方便。但是,它可能会与其他潜在的未来发生语法冲突,尤其是“非本地返回”(允许 lambda 从其封闭方法返回)和“块表达式”(允许表达式中的语句)。虽然我们可以想象那些不冲突的语法,但我们不想在这一点上限制它们的设计空间,至少不是对于仅仅“很高兴拥有”的功能。

此外,虽然我们一直在将其与 throw 表达式类比来讨论,但这并不完全正确。 throw 是一种动态效果,而 return、break 和 continue 是具有特定目标的静态绑定控制传输。

正如讨论所表明的那样,很难想出一个令人信服的示例,而已经有很多替代方案。

在任何情况下,return 表达式都与 throw 表达式不同。异常既不是控制流也不是返回机制。这是一个熔断的保险丝,需要处理,否则应用程序无法继续。

抛出表达式不仅方便,它们还允许在只有表达式有效的地方抛出异常。这就是为什么它们被用于 F# 的 raisefailwith 函数等函数式语言中的原因。 没有它们,像 C# 的 switch expressions 或 F# 的 match expressions 这样的模式匹配结构将无法在编译时编写和分析。

没有 throw 表达式:

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
    };

必须重写以包含一个虚拟的 return 语句,使编译期间的代码分析变得不必要地困难。编译器必须识别这种模式,忽略 return 语句并使用 throw 语句来验证代码是否有效:

public static RGBColor FromRainbow(Rainbow colorBand) =>
    colorBand switch
    {
        Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
        Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
        _              => { 
                              throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand));
                              return default;
                          }
    };

return default 也会对 C# 8 的可空引用类型和可空性分析造成严重破坏。

C# 8 利用 C# 7 的 throw 表达式来提供 switch 表达式。 C# 9 将利用这两者来提供区分联合和(希望)详尽的模式匹配。

【讨论】:

    猜你喜欢
    • 2019-03-27
    • 2016-06-28
    • 2011-04-24
    • 1970-01-01
    • 2019-03-18
    • 2022-12-12
    • 2015-11-28
    • 2021-11-15
    • 2017-11-20
    相关资源
    最近更新 更多