【问题标题】:Benefits of using the conditional ?: (ternary) operator使用条件 ?: (三元)运算符的好处
【发布时间】:2011-03-19 18:33:36
【问题描述】:

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

条件 ?:运算符

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

标准 If/Else

  • 可应用于更多情况(如函数调用)
  • 通常过长

每个语句的可读性似乎有所不同。在第一次接触 ?: 运算符后的一段时间,我花了一些时间来消化它是如何工作的。鉴于我与许多非程序员一起工作,您会建议尽可能使用它,还是坚持使用 if/else?

【问题讨论】:

  • 您已经掌握了要点。
  • @Nicholas Knight:我猜 OP 意味着你不能这样做,例如,SomeCheck() ? DoFirstThing() : DoSecondThing();——你必须使用表达式来返回一个值。
  • 清晰的地方使用它,如果不是,请坚持使用 if/else。代码清晰度应该是您的主要考虑因素。
  • 你见过'??'然而?说真的,如果你认为三元组很酷......
  • +1 因为不像很多人那样简单地称它为“三元运算符”。尽管它是 C# 中唯一的三元(相对于一元和二元)运算符,但这并不是它的名字。

标签: c# conditional-operator


【解决方案1】:

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

在 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。" 你的意思是 确实我> 是什么意思?与您试图解决的问题相比,不正确的 cmets 可能会给新的 C++ 程序员带来更多的困惑;)
  • 对于未来的读者来说,通过“const int speed = (shiftKeyDown) ? 10 : 1;”,这意味着当第一次创建常量 ,它被设置为 10 或 1。它意味着每次访问常量时都会进行检查。 (以防新的 C++ 程序员感到困惑)
  • ...或者换一种说法,const 是常量,即在声明它的语句执行后不能更改。
  • @JaminGrey。不应该是readonly 吗?我一直认为const 的意思是“在编译时解决并在任何使用的地方内联”。
  • @ColinWiseman,这是一个 example 来说明如何使用 ?: 可以。我特别指出,仅仅因为你能做到,并不意味着在任何特定情况下它一定是“最好的”事情。为了解决这个问题,读者应该在每次遇到可能对他们有用的案例时使用他们的大脑。
【解决方案2】:

使用 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 是赋值的左侧,是接收赋值的变量。

【讨论】:

    【解决方案3】:

    有时它可以使 bool 值的赋值一目了然:

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

    【讨论】:

      【解决方案4】:

      虽然上述答案是有效的,并且我同意可读性很重要,但还有 2 点需要考虑:

      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】:

        一个很酷的用法是:

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

        【讨论】:

        • PHP 中注意这一点,三元运算符在 PHP 中的关联方式错误。本质上,如果foo 为假,那么整个事情将评估为 4 而无需进行其他测试。
        • @TomBusby — 哇。另一个讨厌 PHP 的理由,如果你已经讨厌 PHP。
        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

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

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

          【讨论】:

          • 我个人会做aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e); 特别是如果bcde 也需要治疗。
          • 为什么在这个例子中使用条件?只需获取 Abs(a) 并调用 compute() 一次。
          • 是的,我没有创造最好的例子。 :)
          • 对于新手来说,这看起来并不相同。难道不需要 answer = compute(a > 0 ? a, b, c, d, e : -a, b, c, d, e); ?
          • @pbreitenbach:不 - 这是一个优先级问题 - compute(...) 的第一个参数是 a > 0 ? a : -1,所有这些都与其他逗号分隔的参数分开评估。无论如何,不​​幸的是,C++ 缺少您的问题所提出的用于处理逗号分隔值的“元组”的表示法,因此即使 a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e) 也是非法的,并且如果不更改 compute 本身,没有什么非常相似的方法可以工作。
          【解决方案8】:

          如果您需要在同一条件下使用多个分支,请使用 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;
          

          用 if 做这件事很麻烦:

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

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

          【讨论】:

            【解决方案9】:

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

            return someIndex < maxIndex ? someIndex : maxIndex;
            

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

            虽然如果您正在寻找布尔值,这有时可能看起来很合适:

            bool hey = whatever < whatever_else ? true : false;
            

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

            bool hey = (whatever < whatever_else);
            

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:

                条件运算符非常适合短条件,例如:

                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());
                }
                

                所以,最后,它是为您现在提供便利(使用时间更短:?)与为您(和其他人)以后提供便利。这是一个判断电话...但与所有其他代码格式问题一样,唯一真正的规则是保持一致,并对那些必须维护(或评分!)您的代码的人保持视觉上的礼貌。

                (所有代码目测编译)

                【讨论】:

                  【解决方案12】:

                  我建议将三元 (?:) 运算符的使用限制为简单的单行赋值 if/else 逻辑。类似于这种模式的东西:

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

                  可以很容易地转换为:

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

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

                  【讨论】:

                    【解决方案13】:

                    使用三元运算符时要认识到它是表达式而不是语句。

                    在像 scheme 这样的函数式语言中不存在区别:

                    (如果 (> a b) a b)

                    条件 ?:运算符 “似乎不像 if/else 构造那样灵活”

                    在函数式语言中是这样。

                    在使用命令式语言进行编程时,我在通常会使用表达式(赋值、条件语句等)的情况下应用三元运算符。

                    【讨论】:

                      【解决方案14】:

                      我基本上建议只在结果语句非常短并且与 if/else 等效语句相比显着提高简洁性而不牺牲可读性时才使用它。

                      好例子:

                      int result = Check() ? 1 : 0;
                      

                      不好的例子:

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

                      【讨论】:

                      • 好电话,但为了记录,这是“简洁”。
                      • @mquander,你确定吗? merriam-webster.com/dictionary/concise
                      • 我总是从一个简单的开始,随着时间的推移使其变得更加复杂,直到它完全不可读。
                      • 第二个示例中的可读性可以通过更好的格式轻松纠正。但是,正如 OP 所推荐的那样,它归结为可读性和简洁性与冗长性。
                      • 不是 OP 问题的一部分,但需要注意的是,您不能让 return 成为三元运算结果的一部分。例如:check() ? return 1 : return 0; 将不起作用,但 return check() ? 1 : 0; 会。在编程中发现这些小怪癖总是很有趣。
                      【解决方案15】:

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

                      【讨论】:

                        【解决方案16】:

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

                        您可能更感兴趣的是?? operator

                        【讨论】:

                          【解决方案17】:

                          如果我想将变量设置为请求中发送的值(如果已定义)或某个默认值(如果未定义),则在进行 Web 开发时我发现它特别有用。

                          【讨论】:

                          • +1 默认值在 web dev 中是一个很好的例子,一个使用三元运算符的好地方
                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2010-09-18
                          • 2010-09-28
                          相关资源
                          最近更新 更多