为什么不能在 C# 中加入空合并赋值运算符?

Why can't you throw in a null-coalescing assignment operator in C#?

空合并运算符

我最喜欢的 C# 功能之一是空合并运算符,我已经使用了很长时间:

// Simple fallback
var foo = specifiedValue ?? fallbackValue;

// Fetch if not present
foo = foo ?? getAFoo();

// Parameter validation
foo = foo ?? throw new ArgumentNullException(nameof(foo));

// Combination of the two previous examples
foo = foo ?? getAFoo() ?? throw new Exception("Couldn't track down a foo :( ");

空合并赋值运算符

我也喜欢新的 C# 8 运算符,它缩短了“如果不存在则获取”用例:

foo = foo ?? getAFoo(); // null-coalescing
foo ??= getAFoo(); // null-coalescing assignment

问题

我曾希望也许我也可以在参数验证用例中使用空合并赋值运算符,但它似乎是不允许的。这就是我想要做的:

foo = foo ?? throw new ArgumentNullException(nameof(foo)); // Does compile
foo ??= throw new ArgumentNullException(nameof(foo));  // Does not compile

谁能解释为什么空合并赋值运算符适用于“如果不存在则获取”场景而不适用于参数验证场景?

https://dotnetfiddle.net/W8cNPo

免责声明

我意识到 ... ??= throw ... 这件事可能不是最易读的方法。我的问题与其说是 style/readability,不如说是想了解这个运算符的怪癖。

谢谢!

?? 的答案相当简单。它只是按照 documentation:

中所述的方式实现的

The null-coalescing operator ?? returns the value of its left-hand operand if it isn't null; otherwise, it evaluates the right-hand operand and returns its result. The ?? operator doesn't evaluate its right-hand operand if the left-hand operand evaluates to non-null.

??= throw 的答案有点棘手。似乎这对它起作用很有用(或者从开发人员的角度来看可能是一致的),但是 的原因。如果您深入研究 ??= 运算符的 C# 8.0 Null Coalescing Assignment proposal,您会遇到这一行(我强调的):

Otherwise, the type of a ??= b is A. a ??= b is evaluated at runtime as a ?? (a = b), except that a is only evaluated once.

这意味着您的作业将评估为

foo ?? (foo = throw new ArgumentNullException(nameof(foo)))

这在语法上无效。

他们必须那样实施吗?不,他们本可以以不同的方式实施,但他们没有。提案接着说

As with any language feature, we must question whether the additional complexity to the language is repaid in the additional clarity offered to the body of C# programs that would benefit from the feature.

看起来像这里,额外的复杂性被认为是没有必要的,因为替代方案非常好。

借用 Julien 对该问题的评论(谢谢!),meeting notes 提供了更多背景信息

Throw expression on the right-hand side

Should the following be allowed: a ??= throw new Exception? throw expressions are only allowed in certain places right now, so we would have to explicitly add support. It makes more sense if the feature is equivalent to a = a ?? b where b is a throw, than a ?? (a = b).

Conclusion

Not supported