使用条件 ?:(三元) 运算符的好处

107

相对于标准的if-else语句,条件?:运算符有哪些好处和缺点。其中明显的分别是:

条件?:运算符

  • 在处理直接值比较和赋值时更短,更简洁。
  • 似乎不如if/else结构灵活。

标准If/Else

  • 可适用于更多情况(例如函数调用)。
  • 通常冗长而无必要。

对于每个语句,它们的可读性似乎各不相同。在初次接触到?:运算符后的一段时间内,我花了一些时间来理解它的工作方式。您是否建议在任何可能的情况下使用它,还是坚持使用if/else,因为我与许多非程序员一起工作?


8
你已经大致了解了。 - Byron Whitlock
1
@Nicholas Knight:我猜OP的意思是你不能这样做,例如SomeCheck() ? DoFirstThing() : DoSecondThing();--你必须使用表达式来返回一个值。 - Dan Tao
6
如果明确的话,使用它;如果不清楚,就坚持使用if/else if。代码清晰度应该是你的主要考虑因素。 - hollsk
8
你看过“??”吗?说真的,如果你认为三元运算很酷的话…… - pdr
4
请不要像许多人一样简单地称其为“三元运算符”,因为它并不是C#中唯一的三元(与一元和二元相对)运算符的名称。请返回仅翻译后的文本。 - John M Gant
显示剩余5条评论
17个回答

125

我基本上建议仅在结果语句非常短并且与if/else等效语相比具有显着简洁性而不损失可读性时使用它。

好的例子:

int result = Check() ? 1 : 0;

不好的例子:

int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;

5
好的,记录一下,但是正确的词汇是“简洁”。 - mqp
6
@mquander,你确定吗?http://www.merriam-webster.com/dictionary/concise - Byron Whitlock
40
我通常从一个简单的开始,然后逐步使它变得更加复杂,直到变得无法阅读。 - Jouke van der Maas
9
第二个例子中的可读性可以通过更好的格式化来轻松改善。但是,正如原帖所建议的那样,这归结于可读性和简洁性与冗长之间的权衡。 - Nathan Ernst
5
虽然这不是原作者的问题,但需要注意的是,你不能将return作为三元运算符的结果之一。例如:“check() ? return 1 : return 0;” 是行不通的,但“return check() ? 1 : 0;”可以。在编程中发现这些小问题总是很有趣的。 - CSS
显示剩余2条评论

53
这基本上已经被其他答案涵盖了,但是“它是一个表达式”并不能真正解释为什么它如此有用...
在像C++和C#这样的语言中,您可以使用它们定义本地只读字段(在方法体内)。使用传统的if/then语句是不可能实现这一点的,因为只读字段的值必须在单个语句中分配:
readonly int speed = (shiftKeyDown) ? 10 : 1;

不同于:

readonly int speed;  
if (shifKeyDown)  
    speed = 10;    // error - can't assign to a readonly
else  
    speed = 1;     // error  

类似地,您可以将三元表达式嵌入其他代码中。这不仅使源代码更加紧凑(有时也更容易阅读),而且还可以使生成的机器码更加紧凑和高效:

MoveCar((shiftKeyDown) ? 10 : 1);

使用lambda表达式可能会比调用同一个方法两次生成更少的代码:

if (shiftKeyDown)
    MoveCar(10);
else
    MoveCar(1);

当然,这也是一种更方便、更简洁的形式(打字更少,重复更少,如果您需要在if/else中复制代码块,可以减少错误的机会)。在像这样的“常见模式”情况下:

object thing = (reference == null) ? null : reference.Thing;

...使用三元操作符可以使代码更快速地阅读/解析/理解(一旦你习惯了它),相比冗长的if/else等价物,所以它可以帮助你更快地理解代码。

当然,仅仅因为它是有用的并不意味着在每种情况下都是最好的选择。我建议只在短代码中使用它,其中含义明确(或通过使用?:更加清晰)- 如果您在更复杂的代码中使用它,或者将三元操作符嵌套在彼此之内,它会使代码难以阅读。


@JaminGrey “这并不意味着在创建常量时,它被设置为10或1。” 你的意思是“确实”意味着吗?不正确的评论可能会给新的C++程序员带来比你试图澄清的问题更多的困惑;) - Clonkex
5
对于未来阅读者而言,“const int speed = (shiftKeyDown) ? 10 : 1;” 的意思是,当这个常量“首次创建”时,它被设置为10或1。这并不意味着每次访问常量时都会进行检查。(以防新的C ++程序员感到困惑) - Jamin Grey
2
换句话说,const 是一个常量,即在声明它的语句执行后不能被更改。 - Jason Williams
1
@JaminGrey。不应该是readonly吗?我一直认为const意味着“在编译时解析并在使用的地方内联”。 - Nolonar
1
@ColinWiseman,这是一个示例,用来说明如何使用“?:”。我特别强调,仅仅因为你可以这样做,并不意味着在任何特定情况下这都是最好的选择。为了解决这个问题,读者需要每次遇到可能有用的情况时动动脑筋。 - Jason Williams
显示剩余2条评论

14

如果我有很多重复的代码,通常会选择三元操作符。

if (a > 0)
    answer = compute(a, b, c, d, e);
else
    answer = compute(-a, b, c, d, e);

使用三元操作符,可以通过以下方式实现。

answer = compute(a > 0 ? a : -a, b, c, d, e); 

12
如果需要对 bcde 进行处理,我会这样做:aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e); - corsiKa
10
为什么在这个例子中要使用条件语句?只需获取Abs(a),然后调用compute()一次即可。 - Ash
2
是的,我并没有创造最好的样例。 :) - Ryan Bright
对于新手来说,这看起来并不等价。难道不应该是 answer = compute(a > 0 ? a, b, c, d, e : -a, b, c, d, e); 吗? - pbreitenbach
@pbreitenbach:不是的 - 这是一个优先级问题 - compute(...) 的第一个参数是 a > 0 ? a : -1,这个表达式与其他逗号分隔的参数是分开计算的。不过,很遗憾,C++缺乏处理逗号分隔值元组的符号表示法,因此即使 a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e) 也是非法的,没有任何类似的东西可以在不更改 compute 自身的情况下工作。 - Tony Delroy

12

当进行Web开发时,如果我想将一个变量设置为请求中发送的值(如果定义了该变量),或者将其设置为某些默认值(如果未定义),那么我发现这特别有用。


3
在Web开发中,将+1作为默认值是使用三元运算符的好例子。请注意,翻译后的内容必须与原文意思相同,通俗易懂且没有额外的解释。 - Byron Whitlock

11

一个非常酷的用法是:

x = foo ? 1 :
    bar ? 2 :
    baz ? 3 :
          4;

10
在PHP中要小心使用三元运算符,因为它的结合性与常规不同。如果foo为假,则整个表达式将直接计算为4,而不会执行其他判断。请注意避免该问题。 - Tom Busby

7
有时,使用布尔值可以让代码的可读性更高,这在一眼就能看清变量的取值时特别有用:
// With
button.IsEnabled = someControl.HasError ? false : true;

// Without
button.IsEnabled = !someControl.HasError;

6
我建议将三元(?:)运算符的使用限制在简单的单行赋值if/else逻辑上。类似这样的模式:
if(<boolCondition>) {
    <variable> = <value>;
}
else {
    <variable> = <anotherValue>;
}

可以轻松转换成:

<variable> = <boolCondition> ? <value> : <anotherValue>;

在需要使用if/else if/else、嵌套if/else或者导致多行代码评估的if/else分支逻辑的情况下,我建议避免使用三元操作符。在这些情况下使用三元操作符很可能会导致代码难以阅读、混乱且难以管理。希望这可以帮到你。


我也会使用早期返回,并将其封装成一个函数。 - Máxima Alekz

6
条件运算符非常适合短条件,例如:
varA = boolB ? valC : valD;

我偶尔使用它,因为这样写出东西所需的时间更短...不幸的是,这种分支有时会被其他浏览你代码的开发人员忽略。此外,代码通常不会很短,所以我通常通过将 ? 和 : 放在单独的行上来提高可读性,像这样:

doSomeStuffToSomething(shouldSomethingBeDone()
    ? getTheThingThatNeedsStuffDone()
    : getTheOtherThingThatNeedsStuffDone());

然而,使用if/else块的最大优点(也是我更喜欢它们的原因)是,在稍后添加一些附加逻辑到分支中会更容易。

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else {
doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

或者添加另一个条件:
if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else if (shouldThisOtherThingBeDone()){
    doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

因此,现在对您来说(使用更短的方式:?)更方便,而对您(和其他人)以后更方便。这是一个判断调用...但像所有其他代码格式问题一样,唯一真正的规则是保持一致,并对那些必须维护(或评分!)您的代码的人视觉上有礼貌。(所有代码都是目测编译的)

5
使用三元运算符时需要注意它是一个表达式而不是语句。
在Scheme这样的函数式语言中,不存在这种区别:
(if (> a b) a b)
条件?:运算符"似乎不如if/else结构灵活"
但在函数式语言中则存在。
在命令式语言编程时,我通常在通常使用表达式(赋值,条件语句等)的情况下应用三元运算符。

5

虽然上面的回答是有效的,我也同意可读性的重要性,但还有两个需要考虑的进一步点:

  1. 在C#6中,你可以有表达式体方法。

这使得使用三元运算符特别简洁:

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. 当涉及到隐式类型转换时,行为会有所不同。

如果你有类型 T1T2,它们都可以被隐式转换为类型 T,那么下面的代码不会起作用:

T GetT() => true ? new T1() : new T2();

(由于编译器试图确定三元表达式的类型,并且T1T2之间没有转换,因此无法工作。)

另一方面,下面的if / else版本可以正常工作:

T GetT()
{
   if (true) return new T1();
   return new T2();
}

因为T1被转换为T,同时T2也是如此。


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