用通俗易懂的语言解释C#中 | 和 || 的区别是什么?

44

好的,我已经多次阅读了这方面的内容,但我仍然没有听到一种清晰、易于理解(而且易于记忆)的学习以下两个概念之间区别的方法:

if (x | y)
并且。
if (x || y)

在C#的上下文中,...能否有人帮我学习这个基本真理,以及C#如何特别处理它们(因为它们似乎做了同样的事情)。如果一个给定的代码片段在它们之间的差异是无关紧要的,那么我应该默认使用哪个作为最佳实践?

11个回答

81

||是逻辑或(logical-or)运算符。点击这里可以查看详细资料。它的返回值为true,当且仅当至少有一个操作数为true时。你只能将其用于布尔类型的操作数;若使用整型操作数,则会产生错误。

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

|是“或”运算符。请参见这里。如果应用于布尔类型,它将在至少一个操作数为true时评估为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 || ba | b完全相同的,唯一的例外是当a为真时不会计算b。因此,||被称为“短路运算符”。

如果两个代码之间的差异是无关紧要的,那么我应该默认使用哪一个作为最佳实践?

正如所述,差异并不无关紧要,因此这个问题在某种程度上是无意义的。至于“最佳实践”,并没有一个:你只需使用正确的运算符即可。一般来说,人们更喜欢将布尔操作数中的|||相比较,因为你可以确保它不会产生不必要的副作用。

我认为最佳实践绝对是使用||和&&(避免使用|和&)——因为它们在99%以上的情况下都被使用,这是远离标准方法导致错误更加可能的非标准代码写法。即使你需要始终评估两个操作数,我会建议你避免使用|和&并以其他方式实现。 - Ellis

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.

使用逻辑或(||)操作符的短路求值可以编写更短的代码,因为只有在第一个操作数为 true 时才会评估第二个操作数。与其编写如下的代码:

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,则只有第二个操作数会被评估,因此您知道如果评估第二个操作数,则可以安全地使用字符串引用,因为它不可能为空。
在大多数情况下,您应该使用"||"运算符而不是"|"运算符。如果第一个操作数为false,则无需评估第二个操作数即可获得结果。此外,很多人(显然)不知道可以将布尔操作数与"|"运算符一起使用,因此当看到它在代码中以这种方式使用时,他们可能会感到困惑。

4
希望我能给+10分,这是与C#有关最相关的答案。虽然其他答案在技术上是正确的,但它们忽略了问题涉及到C#中的布尔逻辑这一事实。 - Robin Day
那不应该是“str!= null”吗? - Les
不,那部分是正确的,但我认为第一部分是错误的:int a = 0; int b = 0; bool x = (a != 0 || ++b != 0); // 这里b仍然是0,因为"++b != 0"操作数没有被评估但是a == 0,所以第一条语句被评估为false,因此第二部分被评估。 - Davy8
@Davy8:是的,你说得对,这个例子是错的。我已经纠正了。(关于代码格式化;在注释中根本没有格式化。) - Guffa

10

它们不是同一个东西。其中一个是按位或,另一个是逻辑或。

X || Y 是逻辑或,与 "X 或 Y" 相同,适用于布尔值。它通常用于条件语句或测试中。在这种情况下,X 和 Y 可以替换为任何评估为布尔值的表达式。例如:

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

如果两个条件中的任意一个为真,则该子句求值为真。如果第一个条件为真(如果文件存在),则第二个条件不需要并且将不会被评估。

单个管道符号(|)是按位或运算符。要知道这意味着什么,您必须了解计算机中数字的存储方式。假设您有一个保存值为15的16位数量(Int16)。它实际上以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#中使用按位或运算符(单个管道符号)来测试特定一组位中是否打开了一个或多个。如果您有,假设您有一个学生数据库,那么一组独立布尔值或二进制值可能是像男/女、家庭/校园、当前/非当前、已注册/未注册等。 您可以为每个值存储一个位,而不是为每个值存储布尔字段。男/女可能是第1位。已注册/未注册可能是第2位。

然后您可以使用

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

作为一个测试,以查看是否没有开启任何位,除了“学生是男性”的位,这个位被忽略了。什么?好吧,按位或运算对于在任一数字中打开的每个位返回1。如果上面的按位或运算的结果= 0x0001,则表示位字段中没有打开任何位,除了可能第一个位(0x0001),但你不能确定第一个位是否打开,因为它被掩码了。

有相应的&&和&运算符,它们是逻辑与和按位与。它们具有类似的行为。

你可以使用

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

检查位域中的第一位是否已打开。

编辑:我无法相信我因此被投票否决!


在C#中,竖线符号“|”还有另一层含义。这可能就是原因。 - jalf
1
更令人惊讶的是你被投票支持了,因为你的回答与问题完全无关... - Guffa

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

5
与目前大多数答案所说的不同,其含义与C++中并非完全相同。
对于任意两个返回布尔值的表达式A和B,A || B和A | B的作用几乎相同。
A | B会同时计算A和B,如果其中一个为true,则结果为true。
A || B的作用几乎相同,只是它先计算A,然后只有在必要时才计算B。由于整个表达式为true,只要A或B为true,如果A为true,则无需测试B。因此,||会短路,并在可能的情况下跳过评估第二个操作数,而|运算符将始终计算两者。 |运算符很少使用,通常不会有区别。我能想到的唯一常见的情况是:
if ( foo != null || foo.DoStuff()){ // assuming DoStuff() returns a bool

}

这是因为如果左侧测试失败,DoStuff()成员函数就不会被调用。也就是说,如果foo为空,我们就不会在它上面调用DoStuff(),这样就避免了NullReferenceException的出现。
如果我们使用|运算符,无论foo是否为空,DoStuff()都会被调用。
在整数上,只定义了|运算符,它是按位或,正如其他答案所描述的那样。但是,||运算符在整数类型中没有定义,因此在C#中很难混淆它们。

你写的代码是错误的。正确的形式是:if (foo == null || foo.DoStuff())。因此,如果 foo == null,则表达式返回 true 而不会进一步评估;否则,foo 不是 null,因此第一部分返回 false 并且评估第二部分,这可以实现,因为在这种情况下 foo 不为 null。 - Massimiliano Kraus

4

| 是位运算符(数字,整数)。它通过将数字转换为二进制并对应每个数字进行OR操作来工作。然而,在计算机中,数字已经以二进制表示,因此在运行时不需要进行任何转换 ;)

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


3
以下代码在C/C++中可以工作,因为它没有对布尔类型提供一等支持,它将每个具有“on”位的表达式视为真,否则为假。实际上,如果x和y是数值类型,则以下代码在C#或Java中不起作用。
if (x | y) 

所以上述代码的显式版本是:
if ( (x | y) != 0)

在C语言中,任何一个表达式如果其中有一个“On”位,则结果为true。

int i = 8;

if (i) // 在C语言中有效,结果为true

int joy = -10;

if (joy) // 在C语言中有效,结果为true

现在回到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; }
    }

is:

HeyJudeGet
Yes
HeyBack

Jude不会被打印两次,||是一个布尔运算符,在许多C衍生语言中,布尔运算符被短路处理,如果布尔表达式被短路处理,则执行效率更高。
就通俗易懂而言,当你说短路时,例如在||(或运算符)中,如果第一个表达式已经为true,则不需要评估第二个表达式。例如:if(answer == 'y' || answer == 'Y'),如果用户按下小写y,则程序不需要评估第二个表达式(answer == 'Y')。这就是短路。
在上面的示例代码中,X为true,因此||运算符中的Y将不会进一步评估,因此不会出现第二个“Jude”输出。
即使X和Y是布尔类型,请不要在C#中使用此类代码:if (x | y),执行效率低下。

2
强烈建议阅读Dotnet Mob的这篇文章
对于OR逻辑操作,如果任何一个操作数被评估为true,则整个表达式将被评估为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#中的短路评估

2

不深入探讨任何细节,这里是一个真正的外行人版本。

将"|"看作英语中的直接"或(or)"; 将"||"看作英语中的"否则(or else)"。

同样地,将"&"看作英语中的"和(and)";将"&&"看作英语中的"并且(and also)"。

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


2
第一,位运算符作用于两个数字值,并产生第三个值。
如果您有二进制变量。
a = 0001001b;
b = 1000010b;

那么。
a | b == 1001011b;

也就是说,结果中的一位如果在两个操作数中任意一个为1,则该位为1。 (我的例子使用8位数字只是为了清晰起见)

"双竖线" || 是逻辑或运算符,它接受两个布尔值并得出第三个值。


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