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


101

与标准的if-else语句相反,?:运算符的优缺点是什么?显而易见的是:

条件?:运算符

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

标准If / Else

  • 可以应用于更多情况(例如函数调用)
  • 经常不必要地长

每种陈述的可读性似乎各不相同。在第一次接触?:运算符之后的一小段时间里,我花了一些时间来确切地了解它的工作方式。如果我与许多非程序员一起工作,您是否会建议尽可能使用它,还是坚持使用?


8
您已经掌握了要点。
拜伦·惠特洛克

1
@Nicholas Knight:我猜OP意味着您做不到,例如,SomeCheck() ? DoFirstThing() : DoSecondThing();您必须使用表达式返回值。
丹涛

6
在清晰的地方使用它,如果不是,请坚持使用if / else。代码清晰度应该是您的主要考虑因素。
hollsk 2010年

8
你见过'??' 然而?认真地说,如果您认为三元组很酷……
pdr 2010年

3
+1不能像许多人一样简单地称其为“三元运算符”。即使它是C#中唯一的三元(而不是一元和二进制)运算符,也不是它的名字。
John M Gant

Answers:


122

我基本上建议仅在结果语句非常短并且在不牺牲可读性的情况下,比if / else等效项的简洁性大大提高时,才使用它。

好的例子:

int result = Check() ? 1 : 0;

错误的例子:

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

5
好话,但记录在案,那就是“简洁”。
mqp


39
我总是从一个简单的开始,随着时间的推移使它变得更加复杂,直到完全不可读为止。
Jouke van der Maas

9
通过更好的格式可以很容易地纠正第二个示例中的可读性。但是,正如OP所建议的,归结为可读性,简洁性和冗长性。
内森·恩斯特

4
这不是OP问题的一部分,但需要注意的一个重要事实是,您不能return成为三元运算结果的一部分。例如:check() ? return 1 : return 0;将不起作用,但return check() ? 1 : 0;会起作用。在编程中找到这些小怪癖总是很有趣的。
CSS

50

其他答案几乎涵盖了这一点,但是“这是一个表达”并不能真正解释为什么它如此有用...

在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);

...生成的代码可能少于必须两次调用相同方法的代码:

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

当然,它也是一种更方便和简洁的形式(更少的键入,更少的重复,并且如果必须在if / else中重复代码块,则可以减少错误的机会)。在干净的“通用模式”情况下,如下所示:

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

...比起漫长的if / else等价物,读/解析/理解(一旦您习惯了)速度更快,因此它可以帮助您更快地“理解”代码。

当然,仅仅因为它是有用的,并不意味着它是最好的东西在每一种情况下。我建议仅将短代码用于含义清晰(或更清晰)的短代码,?:如果您在更复杂的代码中使用它,或者将三元运算符相互嵌套,则可能会使代码难以阅读。


@JaminGrey “这并不意味着在创建常量时,它将设置为10或1。” 你的意思是当真?不正确的注释可能会给新C ++程序员带来比您尝试解决的问题更混乱的
事情

5
对于将来的读者,通过“ const int speed =(shiftKeyDown)?10:1; ”,这意味着在首次创建常量时,将其设置为10或1。这并不意味着每次访问常量会进行检查。(以防万一新的C ++程序员感到困惑)
Jamin Gray 2013年

2
...或者换句话说,a const是常量,即在声明它的语句执行后不能更改。
杰森·威廉姆斯

1
@JaminGrey。应该不是readonly吗?我一直认为const这意味着“ 在编译时解决并在任何地方使用内联 ”。
Nolonar 2014年

1
@ColinWiseman,这是一个例子来说明如何?可以使用。我明确指出,仅仅是因为您可以做到,但这并不意味着它一定是在任何特定情况下要做的“最佳”事情。为了解决这个问题,读者应该在每次遇到可能对他们有用的情况时动动脑筋。
詹森·威廉姆斯

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
个人而言,我会做aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);特别是如果bcde需要治疗了。
corsiKa

10
为什么在此示例中完全使用条件式?只需获取Abs(a)并调用一次compute()即可。
Ash Ash

2
是的,我没有创造最好的例子。:)
Ryan Bright

对于新手来说,这看起来并不等同。不需要是答案= 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 2010年

11

一个很酷的用法是:

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

10
在PHP中要小心这一点,三元运算符在PHP中关联了错误的方式。本质上,如果foo为假,则整个结果将等于4,而无需进行其他测试。
汤姆·巴斯比

4
@TomBusby-哇。讨厌PHP的另一个原因是,如果您已经讨厌PHP。
托德·雷曼

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之类的功能语言中,不存在区别:

(如果(> ab)ab)

有条件的?:运算符“似乎不如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被转换TT2


5

有时,乍看之下,它会使bool值的分配更容易阅读:

// With
button.IsEnabled = someControl.HasError ? false : true;

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

4

如果我要设置一个值,并且知道这样做总是一行代码,那么我通常会使用三元(条件)运算符。如果我的代码和逻辑将来有可能改变,我会使用if / else,因为其他程序员更清楚。

您可能还感兴趣的是?? 操作员


4

条件运算符的优点是它是一个运算符。换句话说,它返回一个值。由于if是语句,因此无法返回值。


4

我建议将三元(?:)运算符的使用限制为简单的单行分配if / else逻辑。类似于此模式:

if(<boolCondition>) {
    <variable> = <value>;
}
else {
    <variable> = <anotherValue>;
}

可以很容易地转换为:

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

在需要if / else if / else,嵌套if / else或if / else分支逻辑导致对多行求值的情况下,我将避免使用三元运算符。在这些情况下应用三元运算符可能会导致代码不可读,混乱且难以管理。希望这可以帮助。


2

使用?有一些性能好处。例如 MS Visual C ++,但这确实是编译器特定的东西。在某些情况下,编译器实际上可以优化条件分支。


2

我最发现自己使用的场景是默认值,尤其是在返回值中

return someIndex < maxIndex ? someIndex : maxIndex;

这些确实是我唯一觉得不错的地方,但对他们来说我确实喜欢。

虽然如果您正在寻找布尔值,这有时看起来似乎是一件合适的事情:

bool hey = whatever < whatever_else ? true : false;

因为它很容易阅读和理解,但是为了更明显,应该总是抛弃这个想法:

bool hey = (whatever < whatever_else);

2

如果在相同条件下需要多个分支,请使用if:

if (A == 6)
  f(1, 2, 3);
else
  f(4, 5, 6);

如果需要具有不同条件的多个分支,那么如果语句计数激增,则需要使用三进制:

f( (A == 6)? 1: 4, (B == 6)? 2: 5, (C == 6)? 3: 6 );

另外,您可以在初始化中使用三元运算符。

const int i = (A == 6)? 1 : 4;

如果这样做很麻烦:

int i_temp;
if (A == 6)
   i_temp = 1;
else
   i_temp = 4;
const int i = i_temp;

您不能将初始化放在if / else内,因为它会改变范围。但是引用和const变量只能在初始化时绑定。


2

三元运算符可以包含在右值中,而if-then-else则不能;另一方面,if-then-else可以执行循环和其他语句,而三元运算符只能执行(可能是无效的)右值。

在相关说明中,&&和|| 运算符允许使用if-then-else难以实现的某些执行模式。例如,如果一个函数有多个要调用的函数,并且希望在其中任何一个失败的情况下执行一段代码,则可以使用&&运算符很好地完成。在没有该运算符的情况下执行此操作将需要冗余代码,goto或额外的标志变量。


1

使用C#7时,可以使用新的ref locals功能来简化ref兼容变量的条件分配。因此,现在,您不仅可以:

int i = 0;

T b = default(T), c = default(T);

// initialization of C#7 'ref-local' variable using a conditional r-value⁽¹⁾

ref T a = ref (i == 0 ? ref b : ref c);

...但也非常美妙:

// assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals'

(i == 0 ? ref b : ref c) = a;

该行代码分配的值a,以任一bc取决于的值,i



注意
1. r-value是赋值的右侧,即被赋值的值。
2. l-value是赋值的左侧,即接收赋值的变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.