我经常发现自己在做:
foo = foo ?? x;
为什么我不能这样做:
foo ??= x;
编辑:我知道这不是语言的一部分......我的问题是“为什么不”?我发现重复“foo”的必要性令人不快并且可能容易出错。它看起来和以下一样丑陋:
foo = foo + x;
我经常发现自己在做:
foo = foo ?? x;
为什么我不能这样做:
foo ??= x;
编辑:我知道这不是语言的一部分......我的问题是“为什么不”?我发现重复“foo”的必要性令人不快并且可能容易出错。它看起来和以下一样丑陋:
foo = foo + x;
当我想到它时,
foo = foo ?? x
真的只是
foo = foo != null ? foo : x
在那一点上,与 += 的类比开始分崩离析。
没有理由不能有这样的运算符,但我怀疑语言增加的复杂性虽然轻微,但超过了好处(我认为“非常轻微”)。
基本上,该语言的任何添加都有一个相当高的标准。你说你经常使用这个 - 多久一次,真的?虽然我认为??
很方便,但我不能说我使用得非常频繁。
通常,C# 语言设计团队对添加新语法持保守态度。他们说,添加语言的价值必须与增加语言复杂性的成本进行权衡。
Peter Hallam 曾担任 C# 编译器开发负责人一段时间,也是 C# 语言设计团队的成员。尽管他更关注交互式 GUI 功能而不是语言功能,但他写了关于测量新功能的方法,称为Yardstick 。
本文与以下问题特别相关??=
:
通常正确的设计决策是不改变任何东西。这可能是我从观看 Anders 在 C# 语言设计会议上学到的最重要的一课。通常会提出语言的一个领域,在该领域中似乎可以获得一些真正的收获。我们可以防止用户出现常见的编码错误,或者我们可以针对某个问题域提高语言的可用性。然后经过认真思考,通过所有解决问题的选项,答案是……什么都不做!即使问题是有效的,并且看起来我们应该能够添加一些东西来改善这种情况,但经过仔细考虑,没有办法解决问题,而成本不超过解决方案的收益。我见过 Anders 做出的许多最重要和最有价值的决定都是不增加语言复杂性的决定,除非增加的价值证明了复杂性是合理的。在边缘情况下,他几乎总是什么都不选择,而不是添加一些边缘的东西。
??
不是“运算符”,因为它对操作数的值进行“操作”。您应该将其视为一种语言结构。
假设你有:
node = node.next;
那么您是否希望能够执行以下操作?
node .= next;
我认为??=
不存在的原因与.=
不存在的原因相同。
此外,像+=
,*=
等一元运算符在汇编级别有直接对应物。伪组装:
add reg0, 1
mul reg1, 5
这些运算符的存在是“自然的”。
没有执行此操作的操作员。但是,对于该语言的未来版本,这是一个有趣的建议。我怀疑它到目前为止还没有被包括在内,因为合并运算符的主要用例??
是 Nullable 类型并获取如下所示的值:
int? testValue;
return testValue ?? 0;
而不是像您的示例那样在分配操作中。此外,while+=
保证在所有情况下都会修改 foo 的值,但+= 0
不会??=
。
??=
在讨论to的类比是否+=
有意义时,每个人都缺少一个更简单的答案。
问题:为什么你不能foo ??= x;
在 C# 中做?
答:因为每个功能都以负 100 分开始,所以功能一开始不存在,必须有人来实现它们,如果他们添加了该功能,很可能这些资源已经花在对客户群有更大总体利益的事情上:
...考虑设计该功能应如何工作所需的所有时间和精力,确定所有边界条件,然后对其进行编码,为其编写自动化测试,...编写文档和帮助文本,并继续维护功能预期生命周期的代码、测试和文档(在这种情况下可能是永远的)。所有这些资源是否都花在了对客户群有更大总体利益的事情上?(答案总是“是”——每个人和她的嫂子都能想办法完成这句话,“我不敢相信他们把所有时间都浪费在这个愚蠢的功能上而不是修复......” )
我看不出你为什么要使用 ?? 运算符自分配变量。
也许您是foo
根据一些恰好是的数据库值进行设置的NULL
,所以使用 ?? 该分配期间的运算符(消除了对 ??= 的需要):
foo = (int?)rows["possiblyNullCount"] ?? 0;
相对于:
foo = (int?)rows["possiblyNullCount"];
foo = foo ?? 0;
2017 年更新:Null-coalescing assignment 是 C# 语言设计团队正在考虑的一个积极提案。请在https://github.com/dotnet/cshaplang/issues/34投票或加入讨论
主要是因为 +=、-= 等几乎都是事后才添加的,以保持与 C 和 C++ 的兼容性。(*) 因为 ?? 不是 C 或 C++ 的一部分,因此无需添加额外的运算符。
(*) 请注意,在 C++ 中,为类定义运算符时,通常定义 operator+=(),然后基于它实现 operator+()。(这通常是最有效的方式)。但是,在C#中,实现了operator+,编译器会在oper+的基础上自动添加operator+=。这就是为什么我说 += 运算符是事后才想到的。
虽然不完全是您所要求的(它不是 ??=)...您也许可以利用扩展方法。
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;
}
}
我没有花费大量时间思考这不起作用的可能方式,但它似乎确实通过了我的示例应用程序中的气味测试......
我也想念它——Ruby毁了我。这是一个可以在表达式中使用的类似结构(如果您对副作用不过敏):
foo ?? foo = x;
如:
return foo ?? foo = x;
或(仅供说明!):
DoStuff( foo ?? foo = x );
在属性 getter 之外使用这种模式可能不是一个好主意。
这种风格的陈述感觉就像是来自属性 getter 的延迟加载。在这种情况下,我会选择这种语法
private Foo myFoo;
public Foo MyFoo
{
get
{
return myFoo ?? (myFoo = new Foo());
}
}
空合并赋值运算符??=
是在 C# 8.0 中引入的
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#null-coalescing-assignment
因为 C# 中没有这样的 ??= 运算符。
虽然 += 看起来像是用不同语法表示的两个运算符,但它只是一个运算符。