44

好的,所以我已经读过很多次了,但我还没有听到一种清晰、易于理解(且令人难忘)的方式来了解以下之间的区别:

if (x | y)

if (x || y)

..在 C# 的上下文中。任何人都可以帮助我了解这个基本事实,以及 C# 具体如何以不同的方式对待它们(因为它们似乎做同样的事情)。如果给定的一段代码之间的差异无关紧要,我应该默认将哪个作为最佳实践?

4

11 回答 11

81

||逻辑或运算符。见这里。它评估true是否至少有一个操作数为真。您只能将它与布尔操作数一起使用;将它与整数操作数一起使用是错误的。

// Example
var one = true || bar();   // result is true; bar() is never called
var two = true | bar();    // result is true; bar() is always called

|or运算符。见这里。如果应用于布尔类型,则评估true是否至少有一个操作数为真。如果应用于整数类型,则计算结果为另一个数字。如果至少有一个操作数设置了相应的位,则该数字的每个位都设置为 1。

// Example
var a = 0x10;
var b = 0x01;
var c = a | b;     // 0x11 == 17
var d = a || b;    // Compile error; can't apply || to integers
var e = 0x11 == c; // True

对于布尔操作数,a || b相同a | b只有一个例外,如果为真b则不计算。a为此,||被称为“短路”。

如果给定的一段代码之间的差异无关紧要,我应该默认将哪个作为最佳实践?

如前所述,差异并非无关紧要,因此这个问题部分没有实际意义。至于“最佳实践”,没有一个:您只需使用正确的运算符即可。通常,人们更喜欢||布尔|操作数,因为您可以确定它不会产生不必要的副作用。

于 2009-03-26T05:50:03.003 回答
44

当与布尔操作数一起使用时,运算|符是逻辑运算符,就像 一样||,但不同之处在于||运算符进行短路评估而|运算符不进行。

这意味着总是使用|运算符计算第二个操作数,但使用||运算符仅在第一个操作数的计算结果为 false 时才计算第二个操作数。

对于两个运算符,表达式的结果总是相同的,但是如果第二个操作数的求值导致其他东西发生变化,那么只有在使用|运算符时才能保证发生这种情况。

例子:

int a = 0;
int b = 0;

bool x = (a == 0 || ++b != 0);

// here b is still 0, as the "++b != 0" operand was not evaluated

bool y = (a == 0 | ++b != 0);

// here b is 1, as the "++b != 0" operand was evaluated.

运算符的短路评估||可用于编写更短的代码,因为仅当第一个操作数为真时才评估第二个操作数。而不是这样写:

if (str == null) {
   Console.WriteLine("String has to be at least three characters.");
} else {
   if (str.Length < 3) {
      Console.WriteLine("String has to be at least three characters.");
   } else{
      Console.WriteLine(str);
   }
}

你可以这样写:

if (str == null || str.Length < 3) {
   Console.WriteLine("String has to be at least three characters.");
} else{
   Console.WriteLine(str);
}

仅当第一个操作数为 false 时才评估第二个操作数,因此您知道可以安全地在第二个操作数中使用字符串引用,因为如果评估第二个操作数,它不能为 null。

在大多数情况下,您希望使用||运算符而不是|运算符。如果第一个操作数为假,则无需评估第二个操作数即可获得结果。此外,很多人(显然)不知道您可以将|运算符与布尔操作数一起使用,因此当他们在代码中看到它以这种方式使用时会感到困惑。

于 2009-03-26T07:13:29.387 回答
10

它们是不相同的。一种是按位或,一种是逻辑或。

X || Y ,是逻辑或,与“X 或 Y”的含义相同,适用于布尔值。它用于条件或测试。在这种情况下,X 和 Y 可以替换为任何计算结果为布尔值的表达式。例子:

if (File.Exists("List.txt")  ||  x > y )  { ..}

如果两个条件中的任何一个为真,则该子句的计算结果为真。如果第一个条件为真(如果文件存在),则不需要也不会评估第二个条件。

单管道 ( | ) 是按位或。要知道这意味着什么,您必须了解数字是如何存储在计算机中的。假设你有一个 16 位的量 (Int16),它的值是 15。它实际上存储为 0x000F(十六进制),与二进制的 0000 0000 0000 1111 相同。按位或取两个量并将每对对应的位进行或运算,因此如果位在任一量中为 1,则结果中为 1。因此,如果 a = 0101 0101 0101 0101(以十六进制计算为 0x5555)且 b = 1010 1010 1010 1010(即 0xAAAA),则 a | b = 1111 1111 1111 1111 = 0xFFFF。

您可以在 C# 中使用按位 OR(单管道)来测试是否打开了一组特定位中的一个或多个。如果你有 12 个布尔值或二进制值来测试,你可能会这样做,它们都是独立的。假设您有一个学生数据库。一组独立的布尔值可能是诸如男性/女性、家庭/校园内、当前/非当前、已注册/未注册等。您可以存储这些值,而不是为每个值存储一个布尔字段每个只有一点。男性/女性可能是第 1 位。注册/未注册可能是第 2 位。

然后你可以使用

 if ((bitfield | 0x0001) == 0x0001) { ... }

作为一个测试,看看是否没有打开任何位,除了“学生是男性”位,它被忽略了。嗯?好吧,按位或为任一数字中的每个位返回一个 1。如果上述按位或的结果 = 0x0001,则表示位域中没有打开位,可能除了第一位 (0x0001),但您无法确定第一位是否打开,因为它被掩盖。

有一个对应的&&和&,分别是逻辑与和位与。他们有类似的行为。

您可以使用

 if ((bitfield &  0x0001) == 0x0001) { ... }

查看第一个位是否在位域中打开。

编辑:我不敢相信我为此被否决了!

于 2009-03-26T05:47:36.627 回答
5

||很好的答案,但让我补充一点,如果左侧表达式为 ,则不计算右侧表达式 for true。在评估术语为 a) 性能密集型或 b) 产生副作用(罕见)的情况下,请记住这一点。

于 2009-03-26T06:22:09.370 回答
5

与迄今为止大多数答案所说的不同,其含义与 C++ 中的含义并不完全相同。

对于任何两个表达式 A 和 B 计算为布尔值,A || B 和 A | B 做几乎相同的事情。

一个 | B 评估AB,如果其中一个评估为 true,则结果为 true。

一个 || B 做了几乎相同的事情,只是它先评估 A,然后仅在必要时评估 B。由于如果 A 或 B 为真,则整个表达式为真,因此如果 A 为真,则根本不需要测试 B。所以|| 短路,并在可能的情况下跳过评估第二个操作数,其中 | 运算符将始终评估两者。

该| 运算符不经常使用,而且通常不会有所作为。我能想到的唯一常见情况是:

if ( foo != null || foo.DoStuff()){ // assuming DoStuff() returns a bool

}

这是有效的,因为如果左测试失败,则永远不会调用 DoStuff() 成员函数。也就是说,如果 foo 为空,我们不会在其上调用 DoStuff。(这会给我们一个 NullReferenceException)。

如果我们使用 | 运算符,无论 foo 是否为空,都会调用 DoStuff()。

在整数上,只有 | 运算符已定义,并且是按位或,如其他答案所述。|| 虽然没有为整数类型定义运算符,因此很难在 C# 中将它们混淆。

于 2009-03-26T06:45:45.350 回答
4

| 是按位或运算符(数字、整数)。它通过将数字转换为二进制并对每个相应的数字进行 OR 来工作。再说一次,数字已经在计算机中以二进制表示,因此在运行时不会真正发生这种转换;)

|| 是逻辑 OR 运算符(布尔值)。它仅适用于真值和假值。

于 2009-03-26T05:47:56.627 回答
3

以下将在 C/C++ 中工作,因为它没有对布尔值的一流支持,它将每个带有“on”位的表达式视为 true,否则为 false。事实上,如果 x 和 y 是数字类型,则以下代码在 C# 或 Java 中不起作用。

if (x | y) 

所以上面代码的显式版本是:

if ( (x | y) != 0)

在 C 语言中,任何带有“On”位的表达式都为真

诠释 i = 8;

if (i) // 在 C 中有效,结果为真

诠释喜悦=-10;

if (joy) // 在 C 中有效,结果为真

现在,回到 C#

如果 x 和 y 是数字类型,则您的代码:if (x | y)将不起作用。你试过编译它吗?不起作用

但是对于您的代码,我可以假设 x 和 y 是布尔类型,所以它会起作用,所以 | 和 || 对于布尔类型,|| 短路,| 不是。以下内容的输出:

    static void Main()
    {


        if (x | y)
            Console.WriteLine("Get");

        Console.WriteLine("Yes");

        if (x || y)
            Console.WriteLine("Back");

        Console.ReadLine();
    }


    static bool x
    {
        get { Console.Write("Hey");  return true; }
    }

    static bool y
    {
        get { Console.Write("Jude"); return false; }
    }

是:

HeyJudeGet
Yes
HeyBack

裘德不会被打印两次,|| 是一个布尔运算符,许多 C 派生语言的布尔运算符都是短路的,布尔表达式在短路时性能更高。

至于外行术语,当你说短路时,例如在 || (或运算符),如果第一个表达式已经为真,则无需计算第二个表达式。示例:if (answer == 'y' || answer == 'Y'),如果用户按下小 y,程序不需要计算第二个表达式(answer == 'Y')。那是短路。

在我上面的示例代码中,X 为真,所以 || 上的 Y 运算符不会被进一步评估,因此没有第二个“Jude”输出。

即使 X 和 Y 是布尔类型,也不要在 C# 中使用这种代码:if (x | y)。不高效。

于 2009-03-26T07:03:25.760 回答
2

第一个按位运算符处理两个数值并产生第三个数值。

如果你有二进制变量

a = 0001001b;
b = 1000010b;

然后

a | b == 1001011b;

也就是说,如果任一操作数中的某个位也为 1,则结果中的位为 1。(为了清楚起见,我的示例使用 8 位数字)

“双管道”|| 是一个逻辑 OR 运算符,它接受两个布尔值并产生第三个值。

于 2009-03-26T05:52:06.133 回答
2

在不以任何方式、形状或形式深入研究细节的情况下,这是一个真正的外行版本。

想想“|” 在英语中作为直的“或”;想想“||” 在英语中作为“或其他”。

类似地把“&”想象成英文中的“and”;把“&&”想象成英语中的“and also”。

如果您使用这些术语为自己朗读一个表达式,它们通常会更有意义。

于 2009-03-26T12:10:13.603 回答
2

强烈推荐阅读Dotnet Mob的这篇文章

对于 OR 逻辑运算,如果它的任何操作数被评估为 true,那么整个表达式被评估为 true

这是什么|| 操作员会 - 当它发现为真时,它会跳过剩余的评估。虽然 | 运算符评估它的完整操作数以评估整个表达式的值。

if(true||Condition1())//it skip Condition1()'s evaluation
{
//code inside will be executed
}
if(true|Condition1())//evaluates Condition1(), but actually no need for that
{
//code inside will be executed
}

最好使用逻辑运算符的短路版本,无论是 OR(||) 还是 AND(&&) 运算符。


考虑以下代码片段

int i=0;
if(false||(++i<10))//Now i=1
{
//Some Operations
}
if(true||(++i<10))//i remains same, ie 1
{}

这种效应称为副作用,实际上见于短路逻辑运算符表达式的右侧。

参考:C# 中的短路评估

于 2015-04-10T14:41:11.390 回答
1

虽然它已经被正确地说和回答了,但我想我会添加一个真正的外行的答案,因为很多时候这就是我在这个网站上的感觉:)。另外,我将添加 & 与 && 的示例,因为它是相同的概念

| 与 ||

基本上你倾向于使用 || 当您只需要评估第二部分时,如果第一部分为假。所以这:

if (func1() || func2()) {func3();}

是相同的

if (func1())
{
    func3();
}
else 
{
    if (func2()) {func3();}
}

这可能是一种节省处理时间的方法。如果 func2() 需要很长时间来处理,如果 func1() 已经为真,你就不想这样做。

& 与 &&

在 & 与 && 的情况下,情况类似,如果第一部分为 TRUE,则仅评估第二部分。例如这个:

if (func1() && func2()) {func3();}

是相同的

if (func1())
{
    if (func2()) {func3();}}
}

这可能是必要的,因为 func2() 可能首先取决于 func1() 是否为真。如果您使用 & 并且 func1() 评估为 false,则 & 无论如何都会运行 func2(),这可能会导致运行时错误。

外行杰夫

于 2009-03-26T16:02:10.257 回答