一个明确的,外行的解释|之间的区别和||在c#?

时间:2009-03-26 05:43:56

标签: c# bitwise-operators logical-operators

好的,所以我已多次阅读过这篇文章,但我还没有听到一种清晰,易懂(并且令人难忘)的方法来了解它们之间的区别:

if (x | y)

if (x || y)

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

11 个答案:

答案 0 :(得分:82)

|| logical-or 运算符。请参阅 here 。如果至少有一个操作数为真,则计算结果为true。您只能将它与布尔操作数一起使用;将它与整数操作数一起使用是错误的。

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

|运算符。请参阅 here 。如果应用于布尔类型,则如果至少有一个操作数为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 || b与<{1}} 相同,但a | b为真时不会评估b的唯一例外情况。因此,a被称为“短路”。

  

如果给定代码片段之间的差异是无关紧要的,我应将其默认为最佳实践?

如上所述,差异并非无关紧要,因此这个问题部分没有实际意义。至于“最佳实践”,没有一个:您只需使用正确的操作符即可。一般来说,人们赞成||超过||的布尔操作数,因为你可以确定它不会产生不必要的副作用。

答案 1 :(得分: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时才计算第二个操作数,因此您知道可以安全地在第二个操作数中使用字符串引用,因为如果计算第二个操作数,它不能为空。

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

答案 2 :(得分:10)

他们不一样。一个是按位OR,一个是逻辑OR。

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

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

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

单个管道(|)是按位OR。要知道这意味着什么,您必须了解数字如何存储在计算机中。假设您有一个保持值为15的16位数量(Int16)。它实际上存储为0x000F(十六进制),与二进制的0000 0000 0000 1111相同。按位OR取两个量并将每对相应位的OR组合在一起,因此如果该位在任一数量中为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) { ... }

作为测试,看看是否没有打开位,除了“student is male”位,被忽略。咦?好吧,按位OR为每个数字中的每个位返回1。如果按位OR的结果高于= 0x0001,则意味着在位域中没有打开位,除了可能第一位(0x0001),但你无法确定是否第一位位是打开的,因为它被屏蔽了。

有一个相应的&amp;&amp;和&amp;,它是逻辑AND和按位AND。他们有类似的行为。

您可以使用

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

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

编辑:我不敢相信我已经为此投了票!

答案 3 :(得分:5)

答案很好,但我要补充一点,如果左侧表达式为||,则不评估true的右侧表达式。如果评估术语是a)表现密集型或b)产生副作用(罕见),请记住这一点。

答案 4 :(得分:5)

与目前为止的大多数答案不同,其意思是与C ++完全相同。

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

A | B评估 A和B,如果其中一个评估为真,则结果为真。

A || B几乎完全相同,除了它首先评估A,然后只在必要时评估B.如果A或B为真,则整个表达式为真,如果A为真,则不需要对B进行全部测试。所以||短路,并在可能的情况下跳过评估第二个操作数,其中|运营商将始终评估两者。

|操作员不经常使用,而且通常不会有所作为。我能想到的唯一一个常见的例子就是:

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

}

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

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

关于整数,只有|运算符是定义的,并且是按位OR,正如其他答案所描述的那样。 ||虽然operator不是为整数类型定义的,但是很难将它们混合在C#中。

答案 5 :(得分:4)

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

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

答案 6 :(得分:3)

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

if (x | y) 

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

if ( (x | y) != 0)

在C中,任何对其有“On”位的表达式都会导致为真

int i = 8;

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

int joy = -10;

if(joy)//在C中使用vaild,结果为true

现在,回到C#

如果x和y是数字类型,则代码:如果(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在||上操作员不会被进一步评估,因此没有第二个“裘德”输出。

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

答案 7 :(得分:2)

第一个按位运算符处理两个数值,结果为第三个。

如果你有二进制变量

a = 0001001b;
b = 1000010b;

然后

a | b == 1001011b;

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

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

答案 8 :(得分:2)

不以任何方式,形状或形式钻研细节,这里是真正的外行的版本。

想想“|”在英语中直接“或”;想到“||”英语中的“或其他”。

同样想到“&amp;”英语中的“和”;想到“&amp;&amp;”在英语中也是“和”。

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

答案 9 :(得分:2)

强烈建议您阅读Dotnet Mob的this文章

  

对于OR逻辑运算,如果它的任何操作数被评估为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(&amp;&amp;)运算符。

<小时/> 请考虑以下代码段

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

此效果称为 副作用 ,实际上在短路逻辑运算符的表达式右侧看到

参考:Short-circuit Evaluation in C#

答案 10 :(得分:1)

虽然已经说过并且回答正确但我认为我会添加一个真正的外行答案,因为很多时候这就是我在这个网站上的感觉:)。另外,我将添加&amp;的例子。 vs.&amp;&amp;因为它是相同的概念

<强> | vs ||

基本上你倾向于使用||当你只想评估第二部分时,如果它的第一部分为FALSE。所以这个:

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

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

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

<强>&安培; vs&amp;&amp;

在&amp; vs.&amp;&amp;这是类似的情况,你只评估第二部分,如果第一部分是真的。例如:

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

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

这可能是必要的,因为func2()可能依赖于func1()首先为真。如果您使用&amp;和func1()评估为false,&amp;无论如何都会运行func2(),这可能会导致运行时错误。

Jeff the Layman