【问题标题】:Why can't I do ??= in C#?为什么我不能在 C# 中执行 ??=?
【发布时间】:2010-10-08 03:57:39
【问题描述】:

我经常发现自己在做:

foo = foo ?? x;

为什么我做不到:

foo ??= x;

编辑:我知道这不是语言的一部分...我的问题是“为什么不”?我发现重复“foo”的必要性令人不快并且可能容易出错。它看起来和以下一样丑:

foo = foo + x;

【问题讨论】:

  • 除非 C# 语言的设计者之一来到 Stack Overflow,否则您无法得到“为什么?”的权威答案。尝试问一个不同的问题,例如“??= 的替代品有哪些?”或“我如何向 C# 团队提供反馈?”

标签: c# language-features


【解决方案1】:

当我想到它时,

foo = foo ?? x 

真的只是

foo = foo != null ? foo : x

到那时,与 += 的类比开始分崩离析。

【讨论】:

  • 这也是我的想法。我会对这两个陈述的 IL 感兴趣,我认为它们是相同的。然后 ??= 不成立。
  • 您可能对此感兴趣:stackoverflow.com/questions/547249/… 显然 IL 实际上并不完全相同。
【解决方案2】:

没有理由不能有这样的运算符,但我怀疑语言的附加复杂性虽然轻微,但超过了好处(我认为“非常轻微”)。

基本上,该语言的任何附加内容都有一个相当高的门槛。你说你经常使用这个 - 多久一次,真的?虽然我认为?? 很方便,但我不能说我使用得非常频繁。

【讨论】:

  • +1,不记得我上次使用 ??运算符(在 90 KLOC+ 中使用 3 次)
【解决方案3】:

一般来说,C# 语言设计团队对添加新语法持保守态度。他们说,添加语言的价值必须与增加语言复杂性的成本进行权衡。

Peter Hallam 曾担任 C# 编译器开发主管一段时间,也是 C# 语言设计团队的成员。尽管他更关注交互式 GUI 功能而不是语言功能,但他写了关于衡量新功能的方法,称为 Yardstick。

这段文字与??=的问题特别相关:

通常正确的设计决策是 不改变任何东西。这大概是 我学到的最重要的一课 从 C# 中观看 Anders 语言设计会议。通常一个 语言的领域是在 这似乎是一些真实的 可以获得的收益。我们可以 防止用户使用通用编码 错误,或者我们可以改进 某种语言的可用性 问题域。然后想了想 真的很难,通过所有的 解决该问题的选项, 答案是做....什么都没有!甚至 虽然问题是有效的,而且它 看起来我们应该可以添加 改善情况的东西, 仔细思考后没有 解决问题的方法 成本不超过解决方案的收益。 许多最重要和 我见过安德斯的宝贵决定 做出不添加的决定 语言的复杂性,除非 附加值证明了复杂性。 在边缘情况下,他几乎 总是选择添加 边缘的东西。

参考资料:

【讨论】:

    【解决方案4】:

    ?? 不是“运算符”,因为它对操作数的值进行“操作”。您应该将其视为一种语言结构。

    假设你有:

    node = node.next;
    

    然后您希望能够执行以下操作吗?

    node .= next;
    

    我认为??= 不存在的原因与.= 不存在的原因相同。

    此外,+=、*= 等一元运算符在汇编级别有直接对应项。伪组装:

    add reg0, 1
    mul reg1, 5
    

    这些运算符的存在是“自然而然”的。

    【讨论】:

    • 我认为你的意思是“node .= next;”而不是“node .= node;”
    • 运算符和语言结构之间的区别有什么神圣之处? “节点 .= 下一个;”至少有语法上的相似之处,并且一致的语法总是很好。我也不特别关心哪些操作符有直接的汇编器对应物。
    • 我认为汇编类比没有意义。添加 reg0,1 与 x+=1 不同。 load reg0,st+34;add reg0,1;store st+34,reg0 更接近+=的意思。
    • @David Thornley:这是关于污染语言词汇的。恕我直言,编写同一件事的多种不必要的方式越多,语言就会变得越混乱。这难道不是 PHP 在某些圈子中被鄙视的原因之一吗?
    • @joe_mucchiello:我同意我的汇编类比不是一个强有力的论据。我更多地指的是那些一元运算符的可能起源——从汇编的角度来看,它感觉很“自然”。我并不是说 C 代码是这样编译的。
    【解决方案5】:

    没有执行此操作的操作员。但是,对于该语言的未来版本,这是一个有趣的建议。我怀疑它到目前为止还没有被包括在内,因为合并运算符?? 的主要用例是 Nullable 类型并获取如下所示的值:

    int? testValue;
    
    return testValue ?? 0;
    

    而不是像你的例子那样的赋值操作。此外,+= 保证在所有情况下都会修改 foo 的值,但 += 0 和 ??= 不会。

    【讨论】:

    • 我也是这么想的,这将是一个很好的速记。
    • 它会,但我认为考虑到主要用法与 Nullable 类型,它用于在没有实际值的情况下提供默认值,而不是在将值分配给否则为 null 变量。
    • 不存在这样的保证。我可以 += 或 -= x 其中 x 为 0,我可以 *= 或 /= x 其中 x 为 1。 ??= 不比 ??就语法糖而言。
    • @joe:我想你没抓住我的意思。我说过,在所有情况下,但 x 为 0。 ??= 更糟,因为只有在初始值不为空时才会修改该值,而 += 或 -= 的值的修改是基于 x,而不是值接收分配。
    • 因此,+= 或 -= 比 ??= 更有可能导致赋值。 ??是一个条件运算符而不是算术运算符,因此它不适合 += 使用的模式。 -=、*= 等。
    【解决方案6】:

    在讨论 ??= 与 += 的类比是否有意义时,每个人都缺少一个更简单的答案。

    问题:为什么不能在 C# 中使用foo ??= x;?

    答案:因为every feature starts with minus 100 points、features start out nonexistent and somebody has to make them happen 并且很可能如果他们添加了该功能,those resources have been spent on something that would have a greater total benefit to the customer base:

    ...想想所有的时间和精力 有必要设计功能的方式 应该工作,确定所有边界 条件,然后编码 起来,为它写自动化测试,...写下 文档和帮助文本,以及 继续维护代码、测试、 和该功能的文档 预期寿命(在此 情况可能是永远的)。都可以 这些资源已经花在 会有更大的东西 对客户群的总收益? (答案总是 “是的”——每个人和她的嫂子 可以找到一种方法来完成句子, “我不敢相信他们浪费了这一切 时间在这个愚蠢的功能上,而不是 修复...")

    【讨论】:

      【解决方案7】:

      我看不出你为什么要使用 ??运算符自分配变量。

      也许您正在根据某个恰好是 NULL 的数据库值设置 foo,因此使用 ??分配期间的运算符(这消除了对 ??= 的需要):

      foo = (int?)rows["possiblyNullCount"] ?? 0;
      

      相对于:

      foo = (int?)rows["possiblyNullCount"];
      foo = foo ?? 0;
      

      【讨论】:

      • 与数据库无关……我只是喜欢用 null 来表示“还没有设置值”
      【解决方案8】:

      2017 年更新:Null-coalescing assignment 是 C# 语言设计团队正在考虑的一项积极提案。请在https://github.com/dotnet/csharplang/issues/34投票或加入讨论

      【讨论】:

        【解决方案9】:

        主要是因为 +=、-= 等是事后才添加的,以保持与 C 和 C++ 的兼容性。(*) 因为 ??不是 C 或 C++ 的一部分,因此无需添加额外的运算符。

        (*) 请注意,在 C++ 中,为类定义运算符时,通常定义 operator+=(),然后基于它实现 operator+()。 (这通常是最有效的方式)。 但是在C#中,实现了operator+,编译器会在oper+的基础上自动添加operator+=。这就是为什么我说 += 运算符是事后才添加的。

        【讨论】:

        • 并没有让我觉得是事后的想法,而是编译器强制执行的一致性。
        • 嗯,重点是在 C 和 C++ 中,oper+= 的存在是为了提高运行时效率。在 C# 中,它的存在是为了节省几次击键。
        【解决方案10】:

        虽然不完全符合您的要求(不是 ??=)...您也许可以利用扩展方法。

        static void Main(string[] args)
        {
            object foo = null;
            object x = "something";
            Console.WriteLine(foo.NullToValue(x));
        }
        
        public static class ObjectExtensions
        {
            public static object NullToValue(this object obj, object value)
            {
                return obj != null ? obj : value;
            }
        }
        

        我没有花费大量时间来思考这可能不起作用的方式,但它似乎通过了我的示例应用程序中的气味测试...

        【讨论】:

          【解决方案11】:

          我也想念它——Ruby 毁了我。以下是您可以在表达式中使用的类似结构(如果您对副作用不过敏):

          foo ?? foo = x;
          

          如:

          return foo ?? foo = x;
          

          或(仅用于说明!):

          DoStuff( foo ?? foo = x );
          

          在属性 getter 之外使用这种模式可能不是一个好主意。

          【讨论】:

            【解决方案12】:

            【讨论】:

            • 哇,他们花了很长时间
            【解决方案13】:

            这种风格的声明感觉就像是从属性获取器中延迟加载。在这种情况下,我会选择这种语法

            private Foo myFoo;
            public Foo MyFoo
            {
                get
                {
                    return myFoo ?? (myFoo = new Foo());
                }
            }
            

            【讨论】:

            • 但你还是要重复“foo”
            • 我们有时都会重复“foo”。
            • new Foo() 可能很贵
            【解决方案14】:

            因为 C# 中没有这样的 ??= 运算符。

            虽然 += 看起来像是用不同语法表示的两个运算符,但它只是一个运算符。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-05-15
              • 2012-09-24
              • 1970-01-01
              • 1970-01-01
              • 2015-05-12
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多