是否应该使用三元运算符?

205

我个人是三元运算符的支持者:() ? :

我知道它有其适用场合,但我遇到过许多程序员完全反对使用它,还有一些程序员过于频繁地使用它。

你对此有何感觉?你见过哪些使用它的有趣代码?


9
当表达清晰明了时使用它,当它让人感到困惑时则避免使用。这是一个判断的问题。它可以使代码更易读,但仅适用于简单表达式。试图总是使用它与过度避免它一样有害。 - Abel
4
实际上,这是条件操作符。一个类似的问题在http://stackoverflow.com/questions/725973/what-do-fellow-net-developers-think-about-the-conditional-operator/726005上。 - Daniel Daranas
我有时会使用 x = x if x else y,但后来询问了别人的意见,得知它实际上可以简化为 x = x or y(http://stackoverflow.com/questions/18199381/self-referencing-ternary/18199562#18199562)。 - Scruffy
三元运算符可用于 if..else 结构无法使用的地方,例如在返回语句和函数参数中。虽然可以不使用三元运算符来实现相同的功能,但会导致代码变得更长,可执行文件变得更大。 - Arif Burhan
54个回答

268

仅用于简单表达式:

int a = (b > 10) ? c : d;

不要链式或嵌套三元运算符,因为这样很难阅读且容易引起混淆:

int a = b > 10 ? c < 20 ? 50 : 80 : e == 2 ? 4 : 8;

此外,在使用三元运算符时,考虑以提高可读性的方式格式化代码:

Moreover, when using ternary operator, consider formatting the code in a way that improves readability:

int a = (b > 10) ? some_value                 
                 : another_value;

100
完全同意前几句话,但是对于你所提到的“提高可读性”的例子则完全不同意。如果要使用多行语句,为什么不直接使用if语句呢? - Joe Phillips
5
你更喜欢哪种写法:如果else对于简单的决策来说有点啰嗦:int a = 0; if(b > 10) a = some_value; else a = another_value; - marcospereira
48
因为if语句只是一个语句,当你需要的是一个表达式时它就不够用了。 - falstro
2
在某些语言中,@roe 是一个表达式(例如在Scala中),因此 val x = if(true) 0 else 1 是完全合法的。 - om-nom-nom
6
以这个例子为例,这将使它成为一个if表达式,而不是一个if语句,并且本质上与?:运算符相同。 - falstro
显示剩余8条评论

160

由于您无法在每个子表达式上设置断点,因此调试略微困难。 我很少使用它。


61
这是我听过的对三元运算符最好的反驳。我不同意“不易读”的观点(对我来说,这听起来像是人们太懒惰去适应它),但这个反驳实际上很有根据。 - EpsilonVector
5
如果您在三元运算符内执行任何需要调试的操作,那么它很可能被错误使用。我认为三元运算符应该用于简单赋值。如果您需要在三元运算符中调试,并且之后查看分配的值不足以解决问题,则三元运算符并不是您实际遇到的问题。这是操作复杂性的问题。此时将其转换为if语句。 - Julian

82

我喜欢它们,尤其是在类型安全的编程语言中。

我不明白这样做有什么意义:

int count = (condition) ? 1 : 0;

还有比这更难的吗:

int count;

if (condition)
{
  count = 1;
}
else
{
  count = 0;
}

我认为三元运算符比起其他方式可以使一切更简单、更整洁。


6
当变量是常量时,在D或C++中使用三元初始化更加有用。例如:const int count = ...; - deft_code
嗯,你在那里使用不必要的大括号有点误导了 if/else 的用法。 - bobobobo
1
此外,在这种情况下,如果 conditionbool 你可以直接这样做int count = condition; - bobobobo
1
@bobobobo,这个带大括号的if/else是大多数程序员重写三元运算符的方式。 - Andre Figueiredo
2
@bobobobo 如果没有花括号的if/else语句只会带来麻烦。有人很容易添加一行代码,但忘记了需要加上花括号才能按预期执行(将附加行作为块的一部分执行):https://dev59.com/GXRC5IYBdhLWcg3wMeBS#381274 - George Marian
@GeorgeMarian Marian:是的,这就是为什么大多数编程标准都将它们作为要求的原因。相关文章:*从苹果的#gotofail安全漏洞中学习* - “坚持使用花括号甚至更能突出这个错误”。 - Peter Mortensen

48

嵌套的链式语句我并不喜欢,但是简单的链式语句我还是可以接受的。

我在C语言中更倾向于使用它们,因为它们是有价值的if语句,这样可以减少不必要的重复或变量:

x = (y < 100) ? "dog" :
    (y < 150) ? "cat" :
    (y < 300) ? "bar" : "baz";

与其

     if (y < 100) { x = "dog"; }
else if (y < 150) { x = "cat"; }
else if (y < 300) { x = "bar"; }
else              { x = "baz"; }
在像这样的任务中,我发现重构更少,更清晰。另一方面,在使用Ruby时,我更有可能使用 if...else...end,因为它也是一个表达式。

在这种任务中,我发现重构较少,代码更加清晰。

然而,在使用Ruby时,我更倾向于使用if...else...end,因为它也可以作为一个表达式。

x =   if (y < 100) then "dog"
    elif (y < 150) then "cat"
    elif (y < 300) then "bar"
    else                "baz"
    end

(尽管可以承认,对于如此简单的东西,我可能仍然会使用三元运算符。)


我知道第二个例子更冗长,但对我来说比第一个更易读。 - Chrisuu

41
?:运算符只是过程式if语句的一种函数等价物。因此,只要你不使用嵌套的?:表达式,关于任何操作的函数表示的赞成和反对的论点都适用于这里。但是,嵌套三元操作可能会导致代码非常令人困惑(读者可以自行尝试编写处理嵌套三元条件的解析器,您将会感受到其复杂性)。 但是,在许多情况下,保守地使用?:运算符可以使代码比其他方式更容易阅读,例如:
int compareTo(Object object) {
    if((isLessThan(object) && reverseOrder) || (isGreaterThan(object) && !reverseOrder)) {
       return 1;
    if((isLessThan(object) && !reverseOrder) || (isGreaterThan(object) && reverseOrder)) {
       return -1;
    else
      return 0;
}

现在将其与此进行比较:

int compareTo(Object object) {
    if(isLessThan(object))
        return reverseOrder ? 1 : -1;
    else(isGreaterThan(object))
        return reverseOrder ? -1 : 1;
    else
       return 0;
}

代码更加紧凑,语法噪音更少,并且通过明智地使用三元运算符(即仅与reverseOrder属性相关)最终结果并不特别简洁。


我仍然建议在每个if/then/else结构中使用大括号,除了三元运算符之外,所以你的第二个例子在我看来缺少了一些。 - Kris
是的,它是可用的。它就像一个微小的函数,有一个布尔类型的参数,并且可以返回您想要的任何类型!实际上是一个很棒的运算符。 - bobobobo

24

这实际上是一个风格问题,我通常遵循的潜意识规则有:

  • 只评估一个表达式 - 因此foo = (bar > baz) ? true : false,而不是foo = (bar > baz && lotto && someArray.Contains(someValue)) ? true : false
  • 如果我用它来进行显示逻辑,例如: <%= (foo) ? "Yes" : "No" %>
  • 仅在赋值时使用它;永远不要使用流程逻辑(因此永远不要使用(foo) ? FooIsTrue(foo) : FooIsALie(foo) 在三目运算符中使用流程逻辑本身就是一种欺骗,忽略最后一点。

我喜欢它,因为对于简单的赋值操作而言,它既简洁又优雅。


在C#中,如果你在三元运算符内部分配一个委托,然后在之后调用它,你可以将其用于流程控制。嗯,那算是一种流程控制... - Erik Forbes
25
你的前两个例子真的很糟糕。比较结果已经是布尔值,所以你的三元运算符是无用的,只会让代码变得更复杂。 - Trillian
1
@Trillian +1 是的,应该选择一个不同的任务。foo = (bar > baz); 更简单。 - Eric
对于带有一堆子句的布尔返回情况,我喜欢使用三元条件运算符将要求分解成更小的部分,就像在重构时使用早期返回来简化代码一样。 return bar <= baz ? false ! lotto ? false : someArray.Contains(someValue ) - daotoad

18

像很多观点问题一样,答案不可避免地是:“这取决于情况”。

对于类似以下的情况:

return x ? "Yes" : "No";

我认为这比下面的方式 简洁得多(并且对我来说更快解析):

if (x) {
    return "Yes";
} else {
    return "No";
}

如果你的条件表达式很复杂,那么三元运算符不是一个好选择。例如:

x && y && z >= 10 && s.Length == 0 || !foo

不适合使用三目运算符。

另外,如果你是C程序员,GCC实际上有一个扩展允许你排除三目运算符中的if-true部分,像这样:

(链接)

/* 'y' is a char * */
const char *x = y ? : "Not set";

假设y不为NULL,这将把x设置为y。非常棒。


修复了一些句法和语法问题,Sean :-) 最后一段代码缺少 y,而“assign x to y”表示“y = x”,所以我改成了“set x to y”。 - paxdiablo
@Pax:谢谢!我回滚了语法更改,因为我想指出在使用GCC扩展时,您不需要三目运算符的if-true部分。 - Sean Bright
抱歉,没有看到那段话。不过我不知道是否同意这种做法,因为它允许人们编写无法通过ISO标准编译器编译的代码。不过,当GCC成为最后的胜利者时,这就不重要了 :-) - paxdiablo
肯定是巫术... 谁不用GCC呢? :D - Sean Bright

14
我的看法是,只有在需要表达式的情况下才有意义使用三元运算符。
在其他情况下,三元运算符似乎会降低清晰度。

问题在于,在语言中99%的情况下,一个表达式可以被一个函数替代...而且那些避免三元运算符的人甚至更喜欢这种解决方案。 - PierreBdR

11

我会尽可能地在代码中使用三元运算符,除非这使代码变得极难阅读,但这通常只是我的代码需要一点重构的迹象。

有些人认为三元运算符是一个“隐藏”的功能或者有些神秘。我很惊讶,因为当我开始接触C编程时,它就是我学习的第一件事情之一。我认为它并不会降低可读性,它是语言的自然部分。


2
它可能会导致可读性问题,尤其是在嵌套时。 - David Thornley
我认为“极其难以阅读”有点太宽容了,但总的来说我同意你的观点。这并不是什么困难或神秘的事情。 - EpsilonVector

11

按照 圈复杂度 的衡量标准来看,使用 if 语句或三目运算符是等效的。因此从这个角度来看,答案是否定的,复杂度与之前完全相同。

但从可读性、可维护性和 DRY(不要重复自己) 等其他方面来看,二者的选择都可能比另一个更好。


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