开发者

Why does C# && and || operators work the way they do?

开发者 https://www.devze.com 2023-02-14 19:43 出处:网络
here is a tl;dr I come from a C++ background. && is suppose to check if left side is true and right side is true. what does & have anything to do with this? Why is it being used in the &a

here is a tl;dr

I come from a C++ background. && is suppose to check if left side is true and right side is true. what does & have anything to do with this? Why is it being used in the && logic?


I couldnt understand http://msdn.microsoft.com/en-us/library/aa691312%28v=vs.71%29.aspx and asked a question. It took me quite a while to understand and accept this answer. I had to do a lot of follow up reading How does operator overloading of true and false work?

The operation x && y is evaluated as T.false(x) ? x : T.&(x, y)

Why on earth is it doing this? if the false o开发者_运维问答verload returns true and the true operator returns true then y is not evaluated at all. WTF!!!!

I still cant understand it. Because of this being so weird to me it took me a while to understand the answers in the other question. In C# v = 1 && 2; does not work bc you cant do && on ints. In C this returns true (heres code/example http://codepad.org/9iCaqzQ2). If we follow the operation rule above for this we would do

(using 1 && 2)

  • Int.False(1) is false
  • 1 & 2 (== 0)
  • Int.True(0) (== false)

This would get you the wrong results.

So... What is the reasoning for C# doing the && (and ||) operator(s) the way it does.


&& is defined as a short-circuiting operator; if the first operand evaluates false, it is demanded to short circuit there and not evaluate the right hand side. What else would you expect it to do? This allows checks like if(arg != null && arg.Foo) {...} etc.

Your question basically says "if I write an incorrect implementation of a true/false operator, bad things happen"... so; don't do that! either don't write a true/false operator at all, or if you do; do it right...

There are also &and | which do not short-circuit.


As I understand you would prefer a && b being defined as something like ((bool)a)&&((bool)b) instead of what C# uses.

But I think this kind of operator overloading was introduced to support tri-state bools such as bool? and DBBool.

Let's define a few examples for such a type:

With no short circuiting possible:

null && true == null
null && false == false
null || true == true
null || false == null

With short circuiting possible:

false && null == false
true || null == true

The basic idea here is to treat null as unknown value and return null if the result is undetermined and a bool if the result doesn't change no matter what you put into the null argument.

Now you want to define a short circuiting logical and and or on this type. If you do that using the C# true and false operators, both of which return false on a null argument you get the desired behavior. With a c like behavior you don't.

The C# designers probably didn't care about logical and/or on integers like in your example. Integers are no boolean values and as such should not offer logical operators. That bool and integer are the same thing is one of c's historic properties that a new language doesn't need to mirror. And the distinction of bitwise vs logical operators on ints only exists in c due to c's inability to distinguish booleans and integers. This distinction is unnecessary in languages which distinguish these types.

Calling & a bitwise operation is misleading in C#. The essence of && vs & isn't logical vs bitwise and. That isn't determined by which operator you use, but by which types you use. On logical types (bool, bool?, DBBool) both operators are logical, and on integer types & is bitwise and && doesn't make sense, since you can't short-circuit on integers. The essence of && vs & is that the first short-circuits and the second doesn't.

And for the cases where the operators are defined at all this coincides with the c interpretation. And since && isn't defined on integers, because that doesn't make sense with the C# interpretation of && your problem of how && is evaluated on integers does not exist.


C# is more type safe then C++.

Firstly & is overloaded, there are two versions one is the bitwise AND which operates on two integral types (such as int, long) and returns the result of AND each bit from the first argument with the corresponging bit in the second argument. For example 0011 & 1010 == 0010.

The second overload is logical AND which operates on two boolean types (bool), it returns true if and only if both arguments are also true.

Finally you have && which is conditional AND, again it operates two boolean types and returnes true if and only if both arguments are true but it also has the added guarentee that if the first arugment is true the second argument will not be evaluated. This allows you to write things like if(arr != null && arr.Length > 0)... without the short-circuiting behaviour this would give you a null reference exception.

Rules for | and || are similar, | is overloaded for bitwise and logical OR, and || is the conditional OR.

The reason your possible getting confused with C++ behaviour is that integral types are implicitly convertable to bools in C++, 0 is false and anything else is true, so 5 && 5 returns true in C++. In C# intergral types are not implicitly convertable to bools so 5 && 5 is a type error. && and || are also short-circuiting in C++ however, so you shouldn't be suprised at that being the same in C#.


These operators become easier to understand if you translate them into VB.Net, as follows:

&& = AndAlso: IF foo IsNot Nothing AndAlso foo.bar=1 then <>...

|| = OrElse: If foo Is Nothing OrElse foo.bar=0 then ...<>...

That way when you read them in English you really get the idea of what they are trying to acheive.


A perhaps overlooked part of the question here is why doesn't C# automatically cast integers to booleans?

Automatically casting ints to bools (using the rules of C/C++) allow for a accidentally assigning instead of comparing two values. The C# language designers probably wanted to avoid this...

int a, b;
if(a == b)
    { /* They are equal, so execute this code... */ }

if(a = b)
    { /* Were they actually equal? Dunno, but they are now... */ }

or

while(a = b)
    { /* Eternal loop */ }


If you want both sides to be evaluated you need to use the 'standard' & and | operators. Usually you do want the && and || operators though for efficiency


If x == false, then x && y will be always false -> no need to see value of y. If x == true, then x & y happens. This gives you optimized logic operations.

For int it will be something like: T.false(x == true) ? x : T.&(x == true, y == true)

0

精彩评论

暂无评论...
验证码 换一张
取 消