C#中的三元运算符关联性 - 我可以依赖它吗?

时间:2009-11-19 14:11:41

标签: c# .net ternary-operator associativity

啊,难道你不喜欢一个好的三元滥用吗? :)考虑以下表达式:

true ? true : true ? false : false

对于那些现在完全感到困惑的人,我可以告诉你,这评估为 true 。换句话说,它等同于:

true ? true : (true ? false : false)

但这可靠吗?我能否确定在某些情况下不会出现这种情况:

(true ? true : true) ? false : false

有些人可能会说 - 好吧,只需添加括号或完全不使用它 - 毕竟,众所周知,三元运算符是邪恶的!

当然可以,但在某些情况下,它们确实有意义。对于好奇的 - 我正在拧干通过一系列属性比较两个对象的代码。如果我像这样冷写它会很好:

obj1.Prop1 != obj2.Prop1 ? obj1.Prop1.CompareTo(obj2.Prop1) :
obj1.Prop2 != obj2.Prop2 ? obj1.Prop2.CompareTo(obj2.Prop2) :
obj1.Prop3 != obj2.Prop3 ? obj1.Prop3.CompareTo(obj2.Prop3) :
obj1.Prop4.CompareTo(obj2.Prop4)

简明扼要。但它确实取决于三元运算符的相关性,就像第一种情况一样。括号只会使意大利面条脱离它。

那么 - 这是指定的吗?我找不到了。

5 个答案:

答案 0 :(得分:22)

是的,您可以依赖于此(不仅在C#中,而且在所有(我知道)其他语言(except PHP ... go figure)中使用条件运算符)并且您的用例实际上是非常常见的虽然有些人厌恶它,但仍然在练习。

ECMA-334(C#标准)中的相关部分是14.13§3:

  

条件运算符是右关联的,这意味着操作从右到左分组。   [示例:表单a ? b : c ? d : e的表达式计算为a ? b : (c ? d : e)。结束   例子]

答案 1 :(得分:17)

如果你不得不问,不要。阅读代码的任何人都必须经历您所做的相同过程,一遍又一遍,任何需要查看代码的时间。调试这样的代码并不好玩。最终它只会被改为使用括号。

Re:“尝试用括号写下整个​​事情。”

result = (obj1.Prop1 != obj2.Prop1 ? obj1.Prop1.CompareTo(obj2.Prop1) :
         (obj1.Prop2 != obj2.Prop2 ? obj1.Prop2.CompareTo(obj2.Prop2) :
         (obj1.Prop3 != obj2.Prop3 ? obj1.Prop3.CompareTo(obj2.Prop3) :
                                     obj1.Prop4.CompareTo(obj2.Prop4))))

澄清:

  • “如果必须要求,请不要。”
  • “任何人阅读您的代码......”

遵循项目中常见的约定是如何保持一致性,从而提高可读性。认为你可以编写每个人都可读的代码 - 包括那些甚至不懂语言的代码,这是一个愚蠢的错误!

然而,维持项目内的一致性是一个有用的目标,并且不遵循项目的公认惯例会导致辩论减少解决实际问题。那些阅读代码的人应该知道项目中使用的常见和接受的约定,甚至可能是直接在其上工作的其他人。如果他们不了解他们,那么他们应该学习它们,并且应该知道在哪里寻求帮助。

那就是说 - 如果使用没有括号的三元表达式是项目中常见且被接受的约定,那么一定要使用它! 你必须要求表明它在你的项目中并不常见或被接受。如果你想改变你项目中的约定,那么明确地明确无误,将其标记为要讨论的内容其他项目成员,继续前进。这意味着使用括号或使用if-else。

最后一点要思考,如果你的一些代码看起来很聪明:

  

调试是第一次编写代码的两倍。因此,如果您尽可能巧妙地编写代码,那么根据定义,您不够聪明,无法对其进行调试。 - Brian W. Kernighan

答案 2 :(得分:4)

括号中有损于代码可读性的断言是错误的假设。我发现括号表达式更清晰。就个人而言,我会使用括号和/或重新格式化几行来提高可读性。重新格式化多行并使用缩进甚至可以消除对括号的需要。而且,是的,你可以依赖于这样一个事实,即关联的顺序是确定性的,从右到左。这允许表达式以预期的方式从左到右进行评估。

obj1.Prop1 != obj2.Prop1
     ? obj1.Prop1.CompareTo(obj2.Prop1)
     : obj1.Prop2 != obj2.Prop2
           ? obj1.Prop2.CompareTo(obj2.Prop2)
           : obj1.Prop3 != obj2.Prop3
                  ? obj1.Prop3.CompareTo(obj2.Prop3)
                  : obj1.Prop4.CompareTo(obj2.Prop4);

答案 3 :(得分:1)

参考msdn: http://msdn.microsoft.com/en-us/library/ty67wk28%28VS.80%29.aspx

“如果condition为true,则计算第一个表达式并成为结果;如果为false,则计算第二个表达式并成为结果。只评估两个表达式中的一个。”

答案 4 :(得分:1)

x = cond1 ? result1
  : cond2 ? result2
  : cond3 ? result3
  : defaultResult;

VS

if (cond1) x = result1;
else if (cond2) x = result2;
else if (cond3) x = result3;
else x = defaultResult;

我喜欢第一个。

是的,您可以依赖条件运算符关联性。它在手册中,在dcp友情提供的链接上,用一个例子表示为“条件运算符是右关联的”。并且,正如您所建议的那样,我和其他人一致同意,您可以依赖它的事实允许更清晰的代码。