Shortcircuiting operators in C#

T

TB

This is a repost of something I had posted four months ago with no
response, I'm hoping to get some feedback...

From the Microsoft .net Core Reference (pg. 104)
C# shares the AND (short-circuit) operator (&&) with C and C++.
This operator works with two operands, evaluating as 'true' if
both operands are true.

But later (pg. 124)
Behind the scenes, the compiler combines the 'true', 'false',
and '&' operators in the following way to evaluate the &&
operator:

if (Foo.false(a) != true)
return Foo.true(Foo.operator & (a, b));
else
return Foo.true(a);

I have verified this by overriding the 'true', 'false', and '&'
operators
to display tracing information.

Now this raises a couple of issues in my mind...

1) This seems horribly inefficient. For example, if 'a', the left
hand
object, evaluates to false we end up determining it's boolean value
twice in
order to get our return value. The same is true of operator ||.
Wouldn't a much simpler, cleaner, and *more correct* way to do this
be:

&& ||
----------------- -----------------
if (Foo.false(a)) if (Foo.true(a))
return false; return true;
else else
return Foo.true(b); return Foo.true(b);

2) The short-circuit operator is *not* just a simple boolean operation
since it relies on the implementation of operator& (which returns a
new
object). It is possible for both 'a' and 'b' to separately evaluate
as
true but for 'a & b' to evaluate as false. Yes, yes, I know... this
is
probably a sign of a design mistake, but consider the following
example...

struct MyInt
{
private int _val;

public MyInt(int v) { _val = v; }
public Value
{
get { return _val; }
set { _val = value; }
}

static public bool operator true(MyInt a)
{
return a.Value != 0;
}

static public bool operator false(MyInt a)
{
return a.Value == 0;
}

static public MyInt operator &(MyInt a, MyInt b)
{
return new MyInt(a.Value & b.Value);
}
}

i.e. a pretty simple class that basically just encapsulates a single
int.
Now consider this...

MyInt a(7);
MyInt b(32);
if (a)
Console.WriteLine("a is true!"); // <== non-zero is true
else
Console.WriteLine("a is false!");

if (b)
Console.WriteLine("b is true!"); // <== non-zero is true
else
Console.WriteLine("b is false!");

if (a && b)
Console.WriteLine("a && b is true!");
else
Console.WriteLine("a && b is false!"); // (7 & 32) == 0

The C# method of using the bitwise-AND to implement the logical-AND
(short circuiting) leads to this confusing result.

Comments??

-- TB
 
E

Eric Gunnerson [MS]

I agree that that is a very misleading description.

If you look at the C# language spec, section 7.11.1 discussions boolean
conditional operators. It says:

The operation x && y is evaluated as "x ? y: false". In other words, x is
first evaluated and converted to type bool. Then, if x is true, y is
evaluated and converted to type bool, and this becomes the result of the
operation. Otherwise, the result of the operation is false.

Similarly, x || y is evaluated as "x ? true: y"

This is pretty close to what you list at the end of section (1).

As for section (2), C# has this capability because there are nullable types
where a != b is not necessarily equal to !(a == b), because of the third
state (SqlBool is the canonical example of this). We support overloading
true and false so that you can get the right behavior for expressions with
these types, but unless you have that kind of type, you would never need to
overload true and false.

Hope that helps.
--
Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://blogs.gotdotnet.com/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.
 
T

TB

Eric Gunnerson said:
I agree that that is a very misleading description.

If you look at the C# language spec, section 7.11.1 discussions boolean
conditional operators. It says:

The operation x && y is evaluated as "x ? y: false". In other words, x is
first evaluated and converted to type bool. Then, if x is true, y is
evaluated and converted to type bool, and this becomes the result of the
operation. Otherwise, the result of the operation is false.

Similarly, x || y is evaluated as "x ? true: y"

This is pretty close to what you list at the end of section (1).

Hmmm... yes and no. The first part of section 7.11.1 (thanks, by the
way, for pointing this out -- I keep forgetting that the language
specification is there) says that, "when the operands are of types
that do not define an applicable operator & or operator |, but do
define implicit conversions to bool, the operation is processed as
follows:"

So this seems to depend on whether a given object has an implicit
'bool' conversion or not and whether the object defines the &, true,
and false operators. Again, consider the simple test struct:

struct IntWrapper
{
private int m_val;
public IntWrapper(int v) { m_val = v; }
public int Value { get {return m_val;} set {m_val = value;}}

static public bool operator true (IntWrapper iw) {
return iw.Value != 0; }

static public bool operator false (IntWrapper iw) {
return iw.Value == 0; }

static public IntWrapper operator & (IntWrapper a, IntWrapper b)
{
return new IntWrapper(a.Value & b.Value); }
}

The && operator for two IntWrappers goes through the following:
operator false(a)
operator &(a, b) ==> temp
operator true(temp)

Now add the following implicit conversion without changing anything
else:

static public implicit operator bool(IntWrapper iw) {
return iw.Value != 0; }

Performing a && b now yields this slightly different sequence:
operator false(a)
operator &(a, b) ==> temp
operator bool(temp)

Finally, remove our operator true(), operator false(), and operator
&() to get the sequence you describe (as well as a "correct" result):
operator bool(a)
operator bool(b)

One note, it appears that one cannot provide an operator &() without
the corresponding operator true() and operator false() (compiler
error). So it is impossible to tell whether such an operator would
still be invoked in the process of evaluating operator &&().
As for section (2), C# has this capability because there are nullable types
where a != b is not necessarily equal to !(a == b), because of the third
state (SqlBool is the canonical example of this). We support overloading
true and false so that you can get the right behavior for expressions with
these types, but unless you have that kind of type, you would never need to
overload true and false.

Hmmm... I know about nullable types (null being neither true nor
false), but at least for SqlBoolean I cannot think of a single case
where (a != b) is not the same as !(a == b). Granted, the == and !=
operators can return SqlBoolean.Null if either (or both) operands are
SqlBoolean.Null, but the two results are the same.

However, I'll grant that there may well be types where (a != b) is not
logically equivalent to !(a == b) even though I cannot think of any
examples of this offhand :)
Hope that helps.

Yes, if nothing else, it helps point out that operator overloading is
still a potentially hairy thing to do (as it is in C++) and should be
done carefully. Thanks for the feedback!

-- TB
 
J

john bailo

you want to claim that
if (Foo.false(a) != true)
return Foo.true(Foo.operator & (a, b));
else
return Foo.true(a);

is not as efficient as
&&
-----------------
if (Foo.false(a))
return false;
else
return Foo.true(b);

But the first statement can accomplish its goals with evaluation of one part
of the if-else clauses. Where as your implementation would cause both
clauses to fire off many times.
 
E

Eric Gunnerson [MS]

--
Eric Gunnerson

Visit the C# product team at http://www.csharp.net
Eric's blog is at http://blogs.gotdotnet.com/ericgu/

This posting is provided "AS IS" with no warranties, and confers no rights.
TB said:
"Eric Gunnerson [MS]" <[email protected]> wrote in message
I agree that that is a very misleading description.

If you look at the C# language spec, section 7.11.1 discussions boolean
conditional operators. It says:

The operation x && y is evaluated as "x ? y: false". In other words, x is
first evaluated and converted to type bool. Then, if x is true, y is
evaluated and converted to type bool, and this becomes the result of the
operation. Otherwise, the result of the operation is false.

Similarly, x || y is evaluated as "x ? true: y"

This is pretty close to what you list at the end of section (1).

Hmmm... yes and no. The first part of section 7.11.1 (thanks, by the
way, for pointing this out -- I keep forgetting that the language
specification is there) says that, "when the operands are of types
that do not define an applicable operator & or operator |, but do
define implicit conversions to bool, the operation is processed as
follows:"

So this seems to depend on whether a given object has an implicit
'bool' conversion or not and whether the object defines the &, true,
and false operators. Again, consider the simple test struct:

struct IntWrapper
{
private int m_val;
public IntWrapper(int v) { m_val = v; }
public int Value { get {return m_val;} set {m_val = value;}}

static public bool operator true (IntWrapper iw) {
return iw.Value != 0; }

static public bool operator false (IntWrapper iw) {
return iw.Value == 0; }

static public IntWrapper operator & (IntWrapper a, IntWrapper b)
{
return new IntWrapper(a.Value & b.Value); }
}

The && operator for two IntWrappers goes through the following:
operator false(a)
operator &(a, b) ==> temp
operator true(temp)

Now add the following implicit conversion without changing anything
else:

static public implicit operator bool(IntWrapper iw) {
return iw.Value != 0; }

Performing a && b now yields this slightly different sequence:
operator false(a)
operator &(a, b) ==> temp
operator bool(temp)

Finally, remove our operator true(), operator false(), and operator
&() to get the sequence you describe (as well as a "correct" result):
operator bool(a)
operator bool(b)

One note, it appears that one cannot provide an operator &() without
the corresponding operator true() and operator false() (compiler
error). So it is impossible to tell whether such an operator would
still be invoked in the process of evaluating operator &&().
As for section (2), C# has this capability because there are nullable types
where a != b is not necessarily equal to !(a == b), because of the third
state (SqlBool is the canonical example of this). We support overloading
true and false so that you can get the right behavior for expressions with
these types, but unless you have that kind of type, you would never need to
overload true and false.

Hmmm... I know about nullable types (null being neither true nor
false), but at least for SqlBoolean I cannot think of a single case
where (a != b) is not the same as !(a == b). Granted, the == and !=
operators can return SqlBoolean.Null if either (or both) operands are
SqlBoolean.Null, but the two results are the same.

However, I'll grant that there may well be types where (a != b) is not
logically equivalent to !(a == b) even though I cannot think of any
examples of this offhand :)

IIRC, with SqlBoolean, null is neither equal nor not equal to other values.
I could be wrong, however.
 
T

TB

john bailo said:
you want to claim that


is not as efficient as


But the first statement can accomplish its goals with evaluation of one part
of the if-else clauses. Where as your implementation would cause both
clauses to fire off many times.

I'm not sure I see your point. Whether the else-clause is evaluated
or not depends solely on the boolean value for 'a' in both cases
(albeit expressed slightly differently). In the first implementation
the evaluation of 'b' is short-circuited if the else-clause is taken.
In the second implementation the evaluation of 'b' is short-circuited
if the if-clause is taken.

In the first implementation, however, the body of the if-clause
constructs a brand-new, temporary object via operator&() and then
evaluates its boolean value for the final result. Does this not seem
like it would generally be less efficient than the second
implementation?

-- TB
 
T

TB

Eric Gunnerson said:
This posting is provided "AS IS" with no warranties, and confers no rights.
TB said:
"Eric Gunnerson [MS]" <[email protected]> wrote in message
As for section (2), C# has this capability because there are nullable types
where a != b is not necessarily equal to !(a == b), because of the third
state (SqlBool is the canonical example of this). We support overloading
true and false so that you can get the right behavior for expressions with
these types, but unless you have that kind of type, you would never need to
overload true and false.

Hmmm... I know about nullable types (null being neither true nor
false), but at least for SqlBoolean I cannot think of a single case
where (a != b) is not the same as !(a == b). Granted, the == and !=
operators can return SqlBoolean.Null if either (or both) operands are
SqlBoolean.Null, but the two results are the same.

However, I'll grant that there may well be types where (a != b) is not
logically equivalent to !(a == b) even though I cannot think of any
examples of this offhand :)

IIRC, with SqlBoolean, null is neither equal nor not equal to other values.
I could be wrong, however.

True, but nonetheless all cases of (a != b) vs !(a == b) where a and b
are various values for SqlBoolean yield identical results.

-- TB
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top