IF语句中操作符的顺序

3

在必要时,我经常这样做以防止空指针异常

// Example #1
if (cats != null && cats.Count > 0)
{
  // Do something
}

在#1中,我一直认为cats != null需要放在最前面,因为操作顺序是从左到右进行评估。
然而,与示例#1不同,现在如果对象为null或者Count为零,我想要做一些事情,因此我正在使用逻辑OR而不是AND:
// Example #2
if (table == null || table.Rows == null || table.Rows.Count <= 0)
{
  // Do something
}

逻辑比较的顺序重要吗?或者我可以反转顺序并获得相同的结果,例如在示例#3中?

// Example #3
if (table.Rows.Count <= 0 || table.Rows == null || table == null)
{
  // Do something
}

(顺便说一下,我意识到可以像下面这样重新编写#2,但我认为这很混乱,而且我仍然对OR运算符很好奇)

// Example #4
if (!(table != null && table.Rows != null && table.Rows.Count > 0))
{
  // Do something
}
6个回答

11
在您提供的示例中:
if (table == null || table.Rows == null || table.Rows.Count <= 0)
{
  // Do something
}

如果tables为null,则既不会引用table.Rows,也不会引用table.Rows.Count。这是因为在C#逻辑运算符中,操作顺序很重要。 C#逻辑运算符是短路求值的,在从左到右进行评估时,如果任何结果导致其余表达式无效,则其余表达式将不被评估。

考虑以下代码:

bool A()
{
    return false;
}

bool B()
{
    return true;
}

//...

if (A() && B())
{
    // do something
}

AND逻辑运算需要所有元素都为真才会返回真。然而,A() 返回了false,且运行时(或者说编译器在优化步骤中,但是我们不需要担心那个...)并不会对B()进行求值。

同样的,OR(||)表达式也是如此。如果逻辑运算中的任何一个元素为true,则其余部分将不会被执行,按照从左到右的顺序进行求值。


旁注:如果您使用“ And”,VB不会像这样短路运算符。如果您使用“AndAlso”,它将会这样做。 - Jonathan Allen
如果您编辑并仅提供您答案的底部6行(包括回车符),我将标记您为答案。 - JohnB
@JohnB:VB实际上有很多隐藏的好东西,但你真的必须学会它。如果你只是逐字翻译C#代码,那么你永远会感觉像是在用一把黄油刀当螺丝刀。 - Jonathan Allen
1
@Jonathan,这与C#没有什么不同,如果你使用&,它也不会短路(其他C风格的语言也是如此,但在C#中由于其对布尔值的更严格规则,按位与和布尔与的用法更清晰)。 - Jon Hanna
+1:这为读者提供了一种证明的方法。在 A 和 B 中设置断点,看哪个运行。 - rocketsarefast
显示剩余3条评论

3

是的,短路发生在两种情况下,唯一的区别是当LHS为false时,&&会停止(因为整个表达式必须为false),而||在LHS为true时停止(因为整个表达式必须为true)。

您问题中的前两个示例是正确的,如果table或table.Rows为null,则第三个示例将引发异常。


我也这么认为。我加上这个是因为我觉得我可以更清楚地表达,但就个人而言,我会选择迈克尔。 - Jon Hanna

2
请注意,并非所有的C#逻辑运算符都表现出短路行为(如上所示)。
你正在使用条件与运算符(&&),它会进行短路运算。但是,与运算符(&)与条件与运算符完全相同,只是没有短路运算。
对于或运算符和条件或运算符也是如此。
例如:
//无论顺序如何,如果cats为空,这将抛出异常。 if (cats != null & cats.Count > 0){ }
//由于短路行为,这将不会抛出异常。 if (cats != null && cats.Count > 0){ }

0

顺序很重要。这都是关于短路的。对于AND操作,它会在第一个false时停止评估;对于OR操作,它会在第一个true时停止。

它将在第一次评估后进行短路,这就是为什么您不会得到空指针异常的原因。

当您考虑它时,请考虑为每个对象插入null。

if (null== null || null.Rows == null || null.null.Count <= 0)
{
  // Do something
}

然后评估每个布尔值

if( true || null.Rows == null || null.null.Count <=0)

如果你看到 true,那么就停止。如果你遇到了 "null" 语句,你的程序会崩溃。
以下是一个例子,用于说明程序崩溃的情况。
if (null.null.Count <= 0 || null.Rows == null || null== null)
{
  // CRASH... Do something
}

1
这是错误的,它完全相反。对于AND运算,它遇到的第一个false将停止评估,因为整个语句不能为真;对于OR运算,它可以在任何部分为true时停止。 - Stephen P
是的,那是一个小错误...现在已经修复了。 - Nix

0
第一种结构与第二种不同。当您想要在引用为空或计数为零时不执行任何操作时,使用第一种结构。而第二种结构会在其为空或计数为零时执行某些操作... 要使第二种结构与第一种相同,您需要编写以下内容:
 if (table == null || table.Rows == null || table.Rows.Count <= 0 || ) 
 {}
 else
 { 
  // Do something 
 } 

顺序很重要,即使在你写的第二个中,如果在尝试评估table.Rows时table为null,它也会崩溃。


我觉得你误解了我的目标。在第二个“构造”中,当它为空或计数为零时,我想要执行某些操作。 - JohnB
如果是那种情况,那么第一个例子并没有做到这一点,它只有在引用不为空时才会执行某些操作。或者这两个例子本来就不应该代表同一个任务吗?如果是这种情况,那么唯一的问题就是你需要颠倒顺序,因为顺序确实很重要,正如其他人也建议的那样... - Charles Bretana
非常感谢您的帮助,Charles。很抱歉我的问题没有表达清楚,我已经修改了它以更好地说明我的意思。其他人已经回答了我想问的问题。再次感谢。 - JohnB

-1

顺序不重要。

澄清:

您在IF语句中放置语句的顺序不会影响是否进入IF语句...因此也不会影响结果。

正如另一位发帖者指出的那样,这将影响哪些语句被评估,但我认为这不会影响最终结果。

编辑@JonHanna

我想我是从OP声明他在必要时检查NPE(请参见他的帖子的第一行)的角度来看待这个问题的。因此,如果他的代码没有产生运行时错误,则顺序无关紧要。我无法想象为什么有人会思考:“如果此IF语句导致NPE,则它将不会被评估...”


2
一个空指针异常会影响到底线。将问题中的第一个示例顺序颠倒,就会得到这个结果。 - Jon Hanna

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