C# 逻辑顺序和编译器行为
Posted
技术标签:
【中文标题】C# 逻辑顺序和编译器行为【英文标题】:C# logic order and compiler behavior 【发布时间】:2010-09-05 12:54:40 【问题描述】:在 C# 中(并且可以随意回答其他语言),运行时评估逻辑语句的顺序是什么?
例子:
DataTable myDt = new DataTable();
if (myDt != null && myDt.Rows.Count > 0)
//do some stuff with myDt
运行时首先评估哪个语句 -
myDt != null
或:
myDt.Rows.Count > 0
?
编译器是否会向后评估语句?也许当涉及“或”运算符时?
& 被称为逻辑位运算符,将始终计算所有子表达式
什么是何时使用按位运算符而不是“短路布尔值”的好例子?
【问题讨论】:
【参考方案1】:C#:从左到右,如果发现不匹配(评估为假),则停止处理。
【讨论】:
正如其他人所说,&& 运算符的短路发生在 false。【参考方案2】:“C#:从左到右,如果找到匹配项(计算结果为真),则处理停止。”
僵尸羊错了,没有足够的代表投反对票。
问题是关于 && 运算符,而不是 ||运算符。
如果发现 FALSE,则 && 评估将停止。
在 || 的情况下如果找到 TRUE,则评估停止。
【讨论】:
是的。你说得很对。不过,我确实希望以正确的方式解释我的原始答案。我当然无意误导任何人。【参考方案3】:我知道这个问题已经得到解答,但我想补充一点与该主题相关的信息。
在诸如 C++ 之类的语言中,您实际上可以重载 && 和 || 的行为运营商,强烈建议您不要这样做。这是因为当您重载此行为时,您最终会强制对操作的双方进行评估。这做了两件事:
-
它打破了惰性求值机制,因为重载是一个必须调用的函数,因此在调用函数之前对两个参数进行求值。
不能保证所述参数的评估顺序,并且可以是特定于编译器的。因此,对象的行为方式与问题/先前答案中列出的示例中的行为方式不同。
如需了解更多信息,请阅读 Scott Meyers 的书,More Effective C++。干杯!
【讨论】:
【参考方案4】:vb.net
if( x isNot Nothing AndAlso x.go()) then
-
从左到右进行评估
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() 函数中,我还做了一些额外的事情,比如检查套接字是否打开,并且只有当一切正常时,对于 both,我应该继续执行 x.process (y)。
同样,这在 99% 的情况下可能是不需要的,也不是很优雅,这就是为什么我说默认应该使用 AndAlso。
【讨论】:
【参考方案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
其实叫Short Circuit Evaluation
【讨论】:
【参考方案6】: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
【讨论】:
【参考方案7】:请注意,&& 和 & 在计算表达式的多少方面存在差异。
&& 被称为短路布尔 AND,正如其他人所指出的,如果在计算所有子表达式之前可以确定结果,它将提前停止。
& 被称为逻辑位运算符,并且总是计算所有子表达式。
这样:
if (a() && b())
仅当 a 返回 true 时才会调用 b。
但是,这个:
if (a() & b())
将始终同时调用 a 和 b,即使调用 a 的结果为假,因此已知为 假 不管调用 b 的结果如何。
|| 也存在同样的差异。和 |运营商。
【讨论】:
【参考方案8】:某些语言有一些有趣的情况,即表达式以不同的顺序执行。我特别想到了 Ruby,但我确信他们是从其他地方(可能是 Perl)借来的。
逻辑中的表达式将保持从左到右,但例如:
puts message unless message.nil?
上面将评估“message.nil?”首先,如果它的计算结果为假(除非像 if 一样,除非它在条件为假而不是真时执行),“puts message”将执行,它将消息变量的内容打印到屏幕上。
有时这是一种有趣的代码结构方式……我个人喜欢将它用于像上面这样非常短的 1 行。
编辑:
为了更清楚一点,上面同:
unless message.nil?
puts message
end
【讨论】:
【参考方案9】:左边的,如果为空则停止。
编辑:在 vb.net 中,除非您使用 AndAlso,否则它将评估两者并可能引发错误
【讨论】:
【参考方案10】:谦虚所指的概念是运算符重载。在声明中:
if( A && B)
// do something
首先评估A,如果评估为假,则永远不会评估B。这同样适用于
if(A || B)
//do something
首先评估A,如果评估为真,则永远不会评估B。
这个概念,重载,适用于(我认为)所有 C 风格的语言,以及许多其他语言。
【讨论】:
【参考方案11】:不,至少 C# 编译器不能向后工作(在 && 或 || 中)。从左到右。
【讨论】:
【参考方案12】:当一切都在行中时,它们从左到右执行。
当事物嵌套时,它们是从内到外执行的。这可能看起来令人困惑,因为通常“最内层”位于该行的右侧,所以它似乎在倒退......
例如
a = Foo( 5, GetSummary( "Orion", GetAddress("Orion") ) );
事情是这样发生的:
使用文字"Orion"
调用GetAddress
使用文字"Orion"
和GetAddress
的结果调用GetSummary
使用文字5
和GetSummary
的结果调用Foo
将此值分配给a
【讨论】:
【参考方案13】:我喜欢 Orion 的回答。我要补充两点:
-
从左到右仍然首先应用
inner-to-outer 确保在调用函数之前解析所有参数
假设我们有以下示例:
a = Foo(5, GetSummary("Orion", GetAddress("Orion")),
GetSummary("Chris", GetAddress("Chris")));
执行顺序如下:
GetAddress("Orion")
GetSummary("Orion", ...)
GetAddress("Chris")
GetSummary("Chris", ...)
Foo(...)
分配给a
我不能谈论 C# 的法律要求(尽管在写这篇文章之前我确实使用 Mono 测试了一个类似的示例),但是这个顺序在 Java 中是有保证的。
为了完整起见(因为这也是一个与语言无关的线程),有像 C 和 C++ 这样的语言,除非有序列点,否则不能保证顺序。参考文献:1、2。然而,在回答线程的问题时,&&
和||
是 C++ 中的序列点(除非重载;另请参阅 OJ 的出色答案)。举几个例子:
foo() && bar()
foo() & bar()
在&&
的情况下,foo()
保证在bar()
之前运行(如果后者完全运行),因为&&
是一个序列点。在&
的情况下,没有这样的保证(在C 和C++ 中),实际上bar()
可以在foo()
之前运行,反之亦然。
【讨论】:
【参考方案14】:什么是何时使用按位运算符而不是“短路布尔值”的好例子?
假设你有标志,比如文件属性。假设您已将 READ 定义为 4,将 WRITE 定义为 2,并将 EXEC 定义为 1。在二进制中,即:
READ 0100
WRITE 0010
EXEC 0001
每个标志都设置了一个位,并且每个位都是唯一的。位运算符让您可以组合这些标志:
flags = READ & EXEC; // value of flags is 0101
【讨论】:
【参考方案15】:我在某处听说编译器向后工作,但我不确定这是多么真实。
【讨论】:
【参考方案16】:当您特别想要评估所有子表达式时,您使用 & 很可能是因为它们具有您想要的副作用,即使最终结果将是 false 并且因此不会执行您的 then 您的 if 语句的一部分。
注意 & 和 |对位掩码和布尔值都起作用,而不仅仅是位操作。它们按位调用,但在 C# 中它们是为整数和布尔数据类型定义的。
【讨论】:
【参考方案17】:@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
【讨论】:
【参考方案18】:D 编程语言确实left-to-right evaluation with short circuiting 和doesn't 允许&&
and '||' 运算符的重载。
【讨论】:
以上是关于C# 逻辑顺序和编译器行为的主要内容,如果未能解决你的问题,请参考以下文章