In the book C#12 In a Nutshell (p. 259 Chapter Overloading true and false), they provide us with this example to illustrate how to overload the true and false operators.
SqlBoolean a = SqlBoolean.Null;
if (a)
Console.WriteLine ("True");
else if (!a)
Console.WriteLine ("False");
else
Console.WriteLine ("Null");
public struct SqlBoolean
{
public static bool operator true (SqlBoolean x)
{
return x.m_value == True.m_value;
}
public static bool operator false(SqlBoolean x)
{
return x.m_value == False.m_value;
}
public static SqlBoolean operator ! (SqlBoolean x)
{
if (x.m_value == Null.m_value) return Null;
if (x.m_value == False.m_value) return True;
return False;
}
public static readonly SqlBoolean Null = new SqlBoolean (0);
public static readonly SqlBoolean False = new SqlBoolean (1);
public static readonly SqlBoolean True = new SqlBoolean (2);
SqlBoolean (byte value) { m_value = value; }
byte m_value;
}
I’m trying to understand when the false
overload is ever called, and from my understanding (and confirming it by playing with the debugger), it seems that it is never called. why would the false
overload be ever useful then? Since we only ever use while (a)
or while (!a)
, etc … Wouldn’t overloading true
and !
be enough?
3
The C# standard has this note:
Note: Although
true
andfalse
are not used explicitly in expressions (and therefore are not included in the precedence table in §12.4.2), they are considered operators because they are invoked in several expression contexts: Boolean expressions (§12.24) and expressions involving the conditional (§12.18) and conditional logical operators (§12.14). end note
Following those links, you’ll find that the false
operator is used in conditional logical operators:
The operation
x && y
is evaluated asT.false(x) ? x : T.&(x, y)
, whereT.false(x)
is an invocation of theoperator false
declared inT
, andT.&(x, y)
is an invocation of the selectedoperator &
. In other words,x
is first evaluated andoperator false
is invoked on the result to determine ifx
is definitely false. Then, ifx
is definitely false, the result of the operation is the value previously computed forx
. Otherwise,y
is evaluated, and the selectedoperator &
is invoked on the value previously computed forx
and the value computed fory
to produce the result of the operation.
Here’s some test code to show the various times that operators are used:
LoggingBoolean t = new LoggingBoolean(true);
LoggingBoolean f = new LoggingBoolean(false);
Console.WriteLine("Boolean expression");
if (t)
{
Console.WriteLine("In true body");
}
if (f)
{
Console.WriteLine("In false body");
}
Console.WriteLine("Conditional operator");
Console.WriteLine(t ? "true branch" : "false branch");
Console.WriteLine(f ? "true branch" : "false branch");
Console.WriteLine("Conditional && logic");
Console.WriteLine(t && t);
Console.WriteLine(t && f);
Console.WriteLine(f && t);
Console.WriteLine(f && f);
Console.WriteLine("Conditional || logic");
Console.WriteLine(t || t);
Console.WriteLine(t || f);
Console.WriteLine(f || t);
Console.WriteLine(f || f);
internal struct LoggingBoolean
{
public bool Value { get; }
public LoggingBoolean(bool value) => Value = value;
public static bool operator false(LoggingBoolean x)
{
Console.WriteLine("false operator called");
return !x.Value;
}
public static bool operator true(LoggingBoolean x)
{
Console.WriteLine("true operator called");
return x.Value;
}
public static LoggingBoolean operator &(LoggingBoolean x, LoggingBoolean y) =>
new LoggingBoolean(x.Value && y.Value);
public static LoggingBoolean operator |(LoggingBoolean x, LoggingBoolean y) =>
new LoggingBoolean(x.Value || y.Value);
public override string ToString() => $"LoggingBoolean({Value})";
}
Output:
Boolean expression
true operator called
In true body
true operator called
Conditional operator
true operator called
true branch
true operator called
false branch
Conditional && logic
false operator called
LoggingBoolean(True)
false operator called
LoggingBoolean(False)
false operator called
LoggingBoolean(False)
false operator called
LoggingBoolean(False)
Conditional || logic
true operator called
LoggingBoolean(True)
true operator called
LoggingBoolean(True)
true operator called
LoggingBoolean(True)
true operator called
LoggingBoolean(False)
False operator will come into play when there will be short circuiting involved – with &&
operator, when if first expression fails, we can ignore the second.
In such situation, false operator will be invoked, please see the example:
// Need to define this operator in order to leverage short circuiting with &&,
// it will be analogical with ||
public static SqlBoolean operator &(SqlBoolean left, SqlBoolean right)
{
// Implement appropriately, this is just example
return True;
}
And then the test:
var shortCircuitAnd = a && a;