18

在 C# 中(并且可以随意回答其他语言),运行时评估逻辑语句的顺序是什么?

例子:

DataTable myDt = new DataTable();
if (myDt != null && myDt.Rows.Count > 0)
{
    //do some stuff with myDt
}

运行时首先评估哪个语句 -

myDt != null

或者:

myDt.Rows.Count > 0

?

有没有时候编译器会向后评估语句?也许当涉及“或”运算符时?


& 被称为逻辑按位运算符,将始终评估所有子表达式

什么是何时使用按位运算符而不是“短路布尔值”的一个很好的例子?

4

18 回答 18

16

C#:从左到右,如果发现不匹配(评估为假),则处理停止。

于 2008-08-07T20:33:22.847 回答
9

“C#:从左到右,如果找到匹配项(计算结果为真),则处理停止。”

僵尸羊错了,没有足够的代表来投票。

问题是关于 && 运算符,而不是 || 操作员。

如果发现 FALSE,则 && 评估将停止。

在 || 的情况下 如果找到 TRUE,则评估停止。

于 2008-08-08T06:51:14.283 回答
8

我意识到这个问题已经得到解答,但我想再提供一些与该主题相关的信息。

在像 C++ 这样的语言中,您实际上可以重载 && 和 || 的行为。运营商,强烈建议您不要这样做。这是因为当您重载此行为时,您最终会强制对操作的双方进行评估。这做了两件事:

  1. 它打破了惰性求值机制,因为重载是一个必须调用的函数,因此在调用函数之前对两个参数进行求值。
  2. 不能保证所述参数的评估顺序,并且可以是特定于编译器的。因此,对象的行为方式与问题/先前答案中列出的示例中的行为方式不同。

有关更多信息,请阅读 Scott Meyers 的书,更有效的 C++。干杯!

于 2008-08-07T22:23:56.057 回答
6

VB.net

if( x isNot Nothing AndAlso x.go()) then
  1. 从左到右进行评估
  2. AndAlso运算符确保只有当左侧为 TRUE 时,才会评估右侧(非常重要,因为 ifx 什么都不是 x.go 会崩溃)

您可以在 vb 中使用And而不是AndAlso。在这种情况下,左侧也会首先被评估,但无论结果如何,右侧都会被评估。

最佳实践:始终使用 AndAlso,除非您有充分的理由不这样做。


有人在后续询问中为什么或何时会使用 And 而不是 AndAlso(或 & 而不是 &&): 这是一个示例:

if ( x.init() And y.init()) then
   x.process(y)
end 
y.doDance()

在这种情况下,我想同时初始化 X 和 Y。必须初始化 Y 才能使 y.DoDance 能够执行。但是,在 init() 函数中,我还做了一些额外的事情,例如检查套接字是否打开,并且只有在这一切正常的情况下,对于两者,我都应该继续执行 x.process(y)。

同样,在 99% 的情况下,这可能不是必需的,也不优雅,这就是为什么我说默认应该使用AndAlso

于 2008-08-07T20:35:11.657 回答
5

@shsteimer

谦虚所指的概念是运算符重载。在语句中:...首先评估 A,如果评估为 false,则永远不会评估 B。这同样适用于

那不是运算符重载。运算符重载是为让您定义运算符的自定义行为而给出的术语,例如 *、+、= 等。

这将让您编写自己的“日志”类,然后执行

a = new Log(); // Log class overloads the + operator
a + "some string"; // Call the overloaded method - otherwise this wouldn't work because you can't normally add strings to objects.

这样做

a() || b() // be never runs if a is true

实际上叫做短路评估

于 2008-08-07T21:58:21.723 回答
4

ZombieSheep 死定了。唯一可能正在等待的“问题”是,只有在您使用 && 运算符时才会出现这种情况。使用 & 运算符时,每次都会计算两个表达式,无论一个或两个计算结果是否为 false。

if (amHungry & whiteCastleIsNearby)
{
   // The code will check if White Castle is nearby
   // even when I am not hungry
}

if (amHungry && whiteCastleIsNearby)
{
   // The code will only check if White Castle is nearby
   // when I am hungry
}
于 2008-08-07T20:45:43.273 回答
4

请注意,&& 和 & 在计算表达式的多少方面存在差异。

&& 被称为短路布尔 AND,并且正如其他人所指出的那样,如果在计算所有子表达式之前可以确定结果,它将提前停止。

& 被称为逻辑按位运算符,将始终评估所有子表达式。

像这样:

if (a() && b())

只有在a返回true时才会调用b

但是,这:

if (a() & b())

将始终同时调用ab,即使调用a的结果是假的,因此无论调用b的结果如何都知道它是的。

|| 也存在同样的差异。和 | 运营商。

于 2008-08-07T20:52:27.440 回答
4

一些语言有一些有趣的情况,其中表达式以不同的顺序执行。我特别想到了 Ruby,但我确信他们是从其他地方(可能是 Perl)借来的。

逻辑中的表达式将保持从左到右,但例如:

puts message unless message.nil?

以上将评估“message.nil?” 首先,如果它的计算结果为假(除非像 if 一样,除非它在条件为假而不是真时执行),“puts message”将执行,它将消息变量的内容打印到屏幕上。

有时这是一种构建代码的有趣方式......我个人喜欢将它用于像上面这样非常短的 1 行。

编辑:

为了更清楚一点,上面的内容与以下内容相同:

unless message.nil?
  puts message
end
于 2008-08-07T20:54:51.897 回答
2

左边的,如果它为空则停止。

编辑:在 vb.net 中,它将评估两者并可能引发错误,除非您使用 AndAlso

于 2008-08-07T20:31:32.973 回答
2

谦虚所指的概念是运算符重载。在声明中:

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

首先评估 A,如果评估为 false,则永远不会评估 B。这同样适用于

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

首先评估 A,如果评估为真,则永远不会评估 B。

这个概念,重载,适用于(我认为)所有的 C 风格语言,以及许多其他语言。

于 2008-08-07T20:35:32.437 回答
1

不,至少 C# 编译器不能向后工作(在 && 或 || 中)。它从左到右。

于 2008-08-07T21:00:27.473 回答
1

什么是何时使用按位运算符而不是“短路布尔值”的一个很好的例子?

假设您有标志,例如文件属性。假设您已将 READ 定义为 4,将 WRITE 定义为 2,并将 EXEC 定义为 1。在二进制中,即:

READ  0100  
WRITE 0010  
EXEC  0001

每个标志都设置了一个位,并且每个标志都是唯一的。位运算符让您可以组合这些标志:

flags = READ & EXEC; // value of flags is 0101
于 2008-08-07T21:51:00.757 回答
1

当一切都在线时,它们从左到右执行。

当事物嵌套时,它们从内到外执行。这可能看起来令人困惑,因为通常“最里面”是在线的右侧,所以看起来它正在倒退......

例如

a = Foo( 5, GetSummary( "Orion", GetAddress("Orion") ) );

事情是这样发生的:

  • GetAddress用文字调用"Orion"
  • GetSummary用文字"Orion"和结果调用GetAddress
  • Foo用文字5和结果调用GetSummary
  • 将此值分配给a
于 2008-08-07T22:02:36.583 回答
1

我喜欢 Orion 的回答。我要补充两点:

  1. 从左到右仍然首先适用
  2. 从内到外确保在调用函数之前解析所有参数

假设我们有以下示例:

a = Foo(5, GetSummary("Orion", GetAddress("Orion")),
           GetSummary("Chris", GetAddress("Chris")));

以下是执行顺序:

  1. GetAddress("Orion")
  2. GetSummary("Orion", ...)
  3. GetAddress("Chris")
  4. GetSummary("Chris", ...)
  5. Foo(...)
  6. 分配给a

我不能谈论 C# 的法律要求(尽管在写这篇文章之前我确实使用 Mono 测试了一个类似的示例),但这个顺序在 Java 中是有保证的。

只是为了完整性(因为这也是一个与语言无关的线程),有像 C 和 C++ 这样的语言,除非有序列点,否则不能保证顺序。参考文献1、2。然而,在回答线程的问题时,是C++ 中的序列点(除非重载;另请参阅 OJ 的出色答案)。所以一些例子:&&||

  • foo() && bar()
  • foo() & bar()

在这种&&情况下,foo()保证在之前运行bar()(如果后者完全运行),因为&&它是一个序列点。在这种&情况下,没有这样的保证(在 C 和 C++ 中),并且确实bar()可以在之前运行foo(),反之亦然。

于 2008-08-07T22:33:16.833 回答
0

我在某处听说编译器向后工作,但我不确定这是多么真实。

于 2008-08-07T20:34:11.800 回答
0

当您特别想要评估所有子表达式时,您使用 & ,很可能是因为它们具有您想要的副作用,即使最终结果将是错误的,因此不会执行您的then部分if语句。

请注意 & 和 | 对位掩码和布尔值都起作用,而不仅仅是位操作。它们被称为按位,但它们是为 C# 中的整数和布尔数据类型定义的。

于 2008-08-07T22:01:24.493 回答
0

@csmba

有人在后续询问中为什么或何时会使用 And 而不是 AndAlso(或 & 而不是 &&):这是一个示例:

if ( x.init() And y.init()) then
   x.process(y)
end 
y.doDance()

在这种情况下,我想同时初始化 X 和 Y。必须初始化 Y 才能使 y.DoDance 能够执行。但是,在 init() 函数中,我还做了一些额外的事情,比如检查套接字是否打开,并且只有当这一切正常时,我才应该继续执行 x.process(y)。

我相信这相当令人困惑。尽管您的示例有效,但这不是使用的典型案例And(我可能会以不同的方式编写它以使其更清晰)。And&在大多数其他语言中)实际上是按位与运算。您将使用它来计算位操作,例如删除标志位或屏蔽和测试标志:

Dim x As Formatting = Formatting.Bold Or Formatting.Italic
If (x And Formatting.Italic) = Formatting.Italic Then
    MsgBox("The text will be set in italic.")
End If
于 2008-08-20T07:35:22.897 回答
0

The D programming language Does do left-to-right evaluation with short circuiting and doesn't allow overloading of the && and '||' operators.

于 2010-04-14T22:49:01.210 回答