C#条件运算符不是语句?

22

我有一个简单的小代码片段,但它让我感到沮丧:

HashSet<long> groupUIDs = new HashSet<long>();
groupUIDs.Add(uid)? unique++ : dupes++;
编译时会生成如下错误信息:

只有赋值、调用、加减一和新对象表达式可以用作语句

HashSet.Add 的文档中说明其返回一个 bool 值,因此三目运算符 (?) 应该可行, 这看起来是在跟踪添加到哈希集中的唯一项和重复项的完全合法的方式。

当我将其重新格式化为 if-then-else 时,它可以正常工作。

有人能解释一下这个错误吗?是否有办法使用简单的三目运算符来实现这个目的?


1
我更喜欢使用 if 语句。一些答案建议使用虚拟赋值变量,这只会增加混乱。 - bruno conde
4
请注意,三元运算符通常用于选择一个值,而不是选择一个动作。在您的情况下,您正在选择是否增加两个值中的一个。因此,更语义化的做法是使用if-else,因为您正在选择一个动作。 - AaronLS
9个回答

19

根据错误信息,三元运算符不能作为语句使用。您需要做如下更改将其转换为赋值:

int dummy = groupUIDs.Add(uid)? unique++ : dupes++;

话虽如此,我建议只使用if-then-else语句。这样做会更少令人困惑,因为它不涉及创建“神奇”的虚拟变量...


1
这似乎解决了它。在C语言中,一个什么也不做的独立三元运算符没有任何问题。显然,在C#中并非如此。谢谢。 - abelenky
9
可以使用if语句替代三元运算符,而不是为了代码的炫酷而赋值给一个无用的变量,从而降低可读性。 - ANeves
1
一个完整格式的if语句需要8行代码和额外的缩进级别。对于如此简单的操作来说,这似乎是极其浪费的。作为C/C++程序员,我非常喜欢三目运算符,并且认为它不会影响可读性或酷炫程度。 - abelenky
我更喜欢在同一行内打开括号,但你对于行数的看法完全正确。只要这是基于知识做出的选择,你可以随意使用三元运算符和虚拟变量 - 这是一个好选择。 :) - ANeves
@abelenky - 我确实明白你为什么没有把它分配给任何东西,但在 C# 中你必须这样做,虽然我希望你不这样做,有点浪费。我几年前也遇到了同样的问题,真的很困惑...我不认为这是一个愚蠢的错误或者什么的。 - Gabe
3
if(groupsUIDs.Add(uid)) { unique++; } else { dupes++; } 只多了15个字符。 - AaronLS

16

正如其他人指出的那样,条件运算符不是合法的语句表达式。(合法的语句表达式包括赋值、调用、增量、减量和构造函数。)

然而,这里还存在一个风格上的问题。我认为表达式应该对它们的值有用,而语句应该对它们的副作用有用。你遇到的问题是,你有一个只对它的副作用有用的表达式,这是不好的代码味道。

你有一个副作用,因此使用条件语句而不是条件表达式。


1
出于对@EricLippert的尊重,我将其更改为条件语句if-else。从C到C#学习曲线的又一步。 - abelenky
8
我很荣幸,但请不要出于我的缘故而这样做。请为将来需要维护你的代码的人而这样做。 :-) - Eric Lippert

7
你没有给三元运算的结果赋值。
HashSet<long> groupUIDs = new HashSet<long>();
int count = groupUIDs.Add(uid)? unique++ : dupes++;

5
三元运算符不是一个语句。因此,它不能单独用于指令 - 这相当于写成:
"something that is not a statement";

为了更清晰明确,你应该移除三元运算符并使用if语句。

在C和C++中,这是一个完全有效的语句(尽管没有副作用)。显然,在C#中并非如此。结果证明,像@sth建议的那样进行虚拟赋值是正确的答案。 - abelenky
这是一个很好的解释,因为如果你考虑一下三元运算符的工作原理,它会计算出一个单一的值,这个值是从两个选项中选择的。所以一旦计算完成,它就像编写了unique;(在递增后)的值或者dupes;的语句,这将是类似于12345;的东西,但它不是一个有效的语句,因为它只是一个孤立的整数。 - AaronLS
1
@AaronLS: 首先,在递增之前,它将是唯一的值,而不是之后。 其次,在许多语言中,拥有单独的整数是完全有效的。在这方面,C#是不同的。 - abelenky
顺便说一下,我绝对不知道孤立整数可以成为其他语言中的语句 - 而我知道一些语言。人总是在不断学习。 :) - ANeves
@abelenky,你对操作顺序的判断很明智,我想到了后缀表达式,但真正关注的是它会被计算成一个整数。我们可以探究不同语言处理它的方式,例如在英语中,“12345”并不是一个完整的语句,但由于这个问题标记为C#,我认为把重点放在这上面会更有成果 ;) - AaronLS

4
编译器并不在抱怨 Add,而是在抱怨你的条件表达式不是完整语句。
一些语言(如JavaScript)允许您使用条件表达式来分支逻辑,就像您在这里所做的那样,但C#要求您将条件表达式的结果分配给变量。一旦您分配了表达式的结果,您就已经完成了一个完整的语句,编译器就会高兴了。

4
实际上,这是在抱怨三元运算符。 - Mehrdad Afshari
@Andrew 那些不是被认为是增量吗?错误消息中提到了它作为有效语句。 - AaronLS
@abelenky:在这里使用++操作符没有任何问题。问题在于三元表达式本身不是语句:类似groupUIDs.Add(uid) ? 0 : 1;的东西也行不通。 - LukeH

2

您需要使用三元运算符的值来做某些事情...

HashSet<long> groupUIDs = new HashSet<long>();
int newCount = groupUIDs.Add(uid)? unique++ : dupes++;

或者 - 使用if语句。
HashSet<long> groupUIDs = new HashSet<long>();
if (groupUIDs.Add(uid))
   unique++;
else
   dupes++;

1

gmcalab和sr pt是正确的;三元运算符的目的是给你一个结果,就像1 + 1给你2一样。你不能只写:

1 + 1;

这里的混淆(我认为)是你把三元运算符当作函数来思考。


1

语言参考中的三元运算符description说明说:

如果条件为真,则计算第一个表达式并返回结果;如果条件为假,则计算第二个表达式并返回结果。

看起来三元运算符只能在赋值的上下文中使用,尽管语言参考没有明确说明。您不会对结果进行赋值。

我认为将其重写为if/else会更清晰。


它不一定是一个赋值 - 例如,你可以将它传递给另一个方法。 - Jon Skeet
你说得对 - 我太匆忙了,措辞不够严谨。 - Andy Johnson

0
如果这个不可接受,那么你的代码为什么可以?只要使用一个if语句就可以了 :-)
        bool b = false;
        b?callB():callA();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接