【问题标题】:Does the ?? operator guarantee only to run the left-hand argument once?有吗?运算符保证只运行一次左侧参数?
【发布时间】:2013-11-24 17:22:58
【问题描述】:

例如,给定以下代码:

int? ReallyComplexFunction()
{
    return 2; // Imagine this did something that took a while
}

void Something()
{
    int i = ReallyCleverFunction() ?? 42;
}

... 是否保证该函数只会被调用一次?在我的测试中,它只被调用一次,但我看不到任何文档说明我可以依赖这种情况。

编辑

我可以猜到它是如何实现的,但请注意:我们是开发人员。我们不应该在猜测和假设上蒙混过关。例如,所有未来的实现都一样吗?其他平台的语言实现是否相同?这取决于语言的规范以及它提供的保证。例如,未来的或不同的平台实现(不是一个好的,但有可能)可能会在 ?? 实现中做到这一点:

return ReallyComplexFunction() == null ? 42 : ReallyComplexFunction();

如果没有返回null,它将调用ReallyComplexFunction 两次。 (虽然这看起来是一个荒谬的实现,但如果你用一个可以为空的变量替换函数,它看起来很合理:return a == null ? 42 : a

如上所述,我知道在我的测试中它只被调用一次,但我的问题是C# 规范是否保证/指定左侧只会被调用一次? 如果是,在哪里?我在C# Language Specification for ?? operator(我最初在其中寻找我的查询的答案)中看不到任何此类提及。

【问题讨论】:

  • 为什么你认为它会被调用两次?
  • 这不是一个愚蠢的问题。有一些奇怪的情况是左手??可以调用两次,看这里stackoverflow.com/q/6256847/2609288
  • @Baldrick:该问题的答案确定行为是编译器中的错误,即它不是预期的行为。
  • @Baldrick,谢谢。这是你引用的一个有趣的案例。还有助于巩固应该调用一次的假设!
  • 这不是规范。搜索 C# 规范,然后阅读第 7.13 节,它清楚地回答了您的问题:“在运行时,首先评估a。如果a 不为空,则a 成为结果。否则,b 被求值并转换为A 类型,这就是结果。"

标签: c# .net


【解决方案1】:

?? 运算符将评估左侧一次,右侧为零或一次,具体取决于左侧的结果。

你的代码

int i = ReallyCleverFunction() ?? 42;

等价于(这实际上非常接近编译器实际生成的内容):

int? temp = ReallyCleverFunction();
int i = temp.HasValue ? temp.Value : 42;

或者更简单地说:

int i = ReallyCleverFunction().GetValueOrDefault(42);

无论你怎么看,ReallyCleverFunction 只被调用一次。

【讨论】:

    【解决方案2】:

    ?? 运算符与左手无关。 首先运行左手,然后?? 运算符评估其响应。

    在您的代码中,ReallyCleverFunction 只会运行一次。

    【讨论】:

      【解决方案3】:

      它只会被调用一次。如果ReallyCleverFunction的值为null,则使用42,否则使用ReallyCleverFunction的返回值。

      【讨论】:

        【解决方案4】:

        它将计算函数,然后计算?? 运算符的左值(函数的结果)。它没有理由调用该函数两次。

        【讨论】:

          【解决方案5】:

          它被调用一次并且按照文档我相信这应该足以假设它只被调用一次:

          如果操作数不为空,则返回左侧操作数;否则 它返回正确的操作数。

          ?? operator

          【讨论】:

            【解决方案6】:

            它只会运行一次,因为?? 运算符只是一个快捷方式

            你的线路

            int i = ReallyCleverFunction() ?? 42;
            

            相同
            int? temp = ReallyCleverFunction();
            int i;
            if (temp != null)
            {
                i = temp.Value;
            } else {
                i = 42;
            }
            

            编译器完成了艰苦的工作。

            【讨论】:

              【解决方案7】:

              首先,通过the official C# language specification 的第 7.13 节中的推断,答案是是的,它确实保证它只会评估一次

              第 7.13 节始终将a 视为对象,因此它必须只接受函数的返回并在处理中使用它。它说以下关于??运算符(强调):

              • 如果 b 是动态表达式,则结果类型是动态的。在运行时,首先评估 a。 如果 a 不为 null,则将 a 转换为动态,并成为结果。否则,对 b 求值,这将成为结果
              • 否则,如果A 存在并且是可空类型并且存在从b 到A0 的隐式转换,则结果类型为A0。在运行时,首先评估 a。如果 a 不为 null,则将 a 解包为类型 A0,这将成为结果。否则,对 b 求值并转换为 A0 类型,这将成为结果。
              • 否则,如果存在A 并且存在从b 到A 的隐式转换,则结果类型为A。在运行时,首先计算a。如果 a 不为空,则 a 成为结果。否则,b 被求值并转换为类型 A,这将成为结果。
              • 否则,如果b 的类型为B,并且存在从a 到B 的隐式转换,则结果类型为B。在运行时,首先计算a。如果 a 不为空,则将 a 解包为 A0 类型(如果 A 存在且可为空)并转换为 B 类型,这将成为结果。否则,b 被评估并成为结果。

              作为旁注,问题末尾给出的链接不是 C# 语言规范,尽管它在“C# 语言规范”的 Google 搜索中排名第一。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-01-12
                • 1970-01-01
                • 1970-01-01
                • 2023-03-07
                • 2017-07-16
                • 2015-06-12
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多