为什么我不能做?=在C#中?
我经常发现自己在做:
foo = foo ?? x;
为什么我不能这样做:
foo ??= x;
编辑 :我知道这不是语言的一部分……我的问题是“为什么不”? 我发现重复“foo”的必要性令人不愉快并且可能容易出错。 它看起来像丑陋一样:
foo = foo + x;
通常,C#语言设计团队对添加新语法很保守。 他们说,必须权衡语言增加的价值与语言复杂性增加的代价。
Peter Hallam有一段时间是C#编译器开发负责人,也是C#语言设计团队的成员。 他写了一篇关于测量新function的方法,称为Yardstick ,尽管他更关注交互式GUIfunction而不是语言function。
本文与??=
的问题特别相关:
通常,正确的设计决策是不改变任何东西。 这可能是我在C#语言设计会议上观看Anders时学到的最重要的一课。 通常会提出一种语言领域,其中可能会产生一些真正的收益。 我们可以防止用户出现常见的编码错误,或者我们可以提高某个问题域的语言可用性。 在经过深思熟虑之后,通过所有解决问题的方案,答案就是……没事! 即使问题是有效的,看起来我们应该能够添加一些东西来改善这种情况,但经过仔细的反思后,没有办法解决不超过解决方案收益的问题。 我看到Anders做出的许多最重要和最有价值的决定都是决定不增加语言的复杂性,除非增加的价值certificate了复杂性。 在边缘情况下,他几乎总是选择添加一些边缘的东西。
参考文献:
- 码尺
- 程序员真正做了什么?
当我想到它时,
foo = foo ?? x
真是公正
foo = foo != null ? foo : x
在这一点上,+ =的类比开始分崩离析。
没有理由不能有这样的运营商,但我怀疑语言的复杂程度虽然微不足道,但却超过了利益(我认为“非常轻微”)。
基本上任何语言的添加都有很高的清晰度。 你说你经常使用这个 – 经常,真的吗? 虽然我认为??
方便,我不能说我甚至经常使用它。
??
它不是一个“运算符”,因为它使用操作数的值进行“操作”。 您应该将其视为语言构造 。
假设你有:
node = node.next;
那么您是否希望能够执行以下操作?
node .= next;
我认为??=
不存在的原因与.=
不存在相同。
此外,像+=
, *=
等的一元运算符在汇编级别具有直接对应的运算符。 伪汇编:
add reg0, 1 mul reg1, 5
这些运营商的存在只是“自然”。
没有操作员执行此操作。 但是,对于该语言的未来版本,这是一个有趣的建议。 我怀疑到目前为止还没有包括它,因为聚结操作符的主要用例是??
,使用Nullable类型并获取值,如:
int? testValue; return testValue ?? 0;
而不是像你的例子那样的赋值操作。 此外,虽然+=
保证在所有情况下都修改foo的值,但+= 0
, ??=
不会。
在讨论??=
to +=
的类比是否有意义时,每个人都缺少一个更简单的答案。
问题:为什么你不能做foo ??= x;
在C#?
答:因为每个function都以负100分 开始 ,所以function开始不存在,有人必须让它们发生,并且最有可能的是,如果他们添加了该function, 那些资源已用于对客户群有更大总收益的事情 :
…想想设计function应该如何工作所需的所有时间和精力,确定所有边界条件,然后对其进行编码,为其编写自动化测试,…编写文档和帮助文本,并继续维护function预期寿命的代码,测试和文档(在这种情况下可能是永久的)。 是否所有这些资源都花在了对客户群有更大利益的事情上? (对此的回答总是“是” – 每个人和她的嫂子都能找到完成判决的方法,“我不敢相信他们一直在浪费这个愚蠢的特征而不是修复……” )
我没有看到为什么你会使用??的原因很多? 运算符自我赋值变量。
也许你根据一些碰巧为NULL
数据库值设置foo
,所以使用??更有意义? 该赋值期间的运算符(不需要?? =):
foo = (int?)rows["possiblyNullCount"] ?? 0;
而不是:
foo = (int?)rows["possiblyNullCount"]; foo = foo ?? 0;
主要是因为+ =, – =等几乎被添加,以保持与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之外使用这种模式可能不是一个好主意。
2017年更新 :Null-coalescing assignment是C#语言设计团队正在考虑的一项活跃提案。 请通过https://github.com/dotnet/csharplang/issues/34投票或加入讨论
这种风格的陈述感觉就像是财产吸气者的懒惰。 在这种情况下,我会选择这种语法
private Foo myFoo; public Foo MyFoo { get { return myFoo ?? (myFoo = new Foo()); } }
因为C#中没有这样的?? =运算符。
虽然+ =可能看起来像两个运算符用不同的语法表示它只是一个运算符。