1

Out of curiosity, I explored overloading operators in C#. There is an interesting post by Eric Lippert discussing the design decisions in C++ and C# concerning operator&&. In C# its overloads are defined implicitly by overloading operator& plus overloading the boolean operators trueand false, which allows the language to preserve the sometimes essential short-circuit semantics.

Playing with the somewhat exotic operator true() and false() I found that I was not able to call them directly. Instead, they are implicitly called in certain places where a bool is required. As far as I can see, these are the language constructs which directly require a bool, namely the ternary conditionaly operator and the if clause, plus calls to operator&& resp. || when the argument type has overloaded operators & resp. |.

Edit: The book "The C# Programming Language" (7.11) as well as the annotated C# Standard in 14.11.2 -- both found via google search result -- have a code example with a direct operator call which I didn't understand must be pseudo code. I tried to replicate that.

As an aside, it is harder to provoke a call to operator false(); the ternary conditional as well as an if clause always test by calling operator true(). It seems as if the only way to call it is by calling operator||().

The motivation to call the boolean operators explicitly is that it would be nice to define only one of them directly and define the other one in terms of that, so that the definitions are always consistent. Below is a little example program with a few things I tried. Is there a syntax which I missed?

using System;

namespace TriviallyTrue
{
    public class T
    {
        public static bool operator true(T t) { Console.WriteLine("In op. true");  return true; }
        public static bool operator false(T t) { return true; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            T t = new T();
            // bool b = T.true(t); // Identifier expected; 'true' is a keyword

            // ok, I see. Let's use the keyword syntax.
            // bool b = T.@true(t); //'TriviallyTrue.T' does not contain a definition for 'true'
                            // That's so not true!

            // oh. perhaps we need to use cast syntax, akin to invoking operator int()?
            // bool b = (true)t; // ; expected

            // hm. It's in T's namespace...
            // bool b = (T.true)t; // Identifier expected;
                    // we know that.

            // another cast try.
            // bool b = (T.@true)t; // The type name 'true' does not exist in the type 'TriviallyTrue.T'
                    // ah, a type is expected. Well, the type is bool, right? But casting to bool
                    // doesn't work either, see below and in Main().

            // bool b = (T.@bool)t; // The type name 'bool' does not exist in the type 'TriviallyTrue.T' 
                    // well, it exists *some*what




            if (t) // works
            {
                // Console.WriteLine("t was " + (bool)t); // Cannot convert type 'TriviallyTrue.T' to 'bool'
                            // That's so not true!
                Console.WriteLine("t was " + (t ? "True" : "False" )); // works!
            }
        }
    }
}

Sample session:

In op. true
In op. true
t was True
Community
  • 1
  • 1
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • Yes, the samples in the spec are intended to be pseudo-code that illustrates the control flow, not actually legal C#. I agree it's a bit confusing. – Eric Lippert Jan 15 '16 at 14:39
  • @EricLippert Just to mention -- the examples do a good job, I'm all for them. (And what I read of the standard was very readable in general.) What I did is probably a recipe for misunderstandings: to just read a few sentences of a single page without any context. – Peter - Reinstate Monica Jan 15 '16 at 15:27

2 Answers2

3

I cannot answer the question in the title, but I think I can cover this part

The motivation to call the boolean operators explicitly is that it would be nice to define only one of them directly and define the other one in terms of that, so that the definitions are always consistent.

Without questioning in any way what @Eric Lippert wrote in that post, C# has an easier way of doing all that when a one of the true or false is logically the inverse on the other, which is the most common practical case. Instead of overriding 4 operators (false, true, & and |), one can simply provide a single implicit conversion to bool.

For instance

public class T
{
    public static implicit operator bool(T t) { return t != null; }
}

Now all these work

T a = new T(), b = null;
if (a) { }
if (!a) { }
if (b) { }
if (!b) { }
if (a && b) { }
if (b && a) { }
if (a & b) { }
if (a || b) { }
if (b || a) { }
if (a | b) { }
var c1 = a ? 1 : 0;
var c2 = b ? 1 : 0;
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
  • Good point, for all types which have no third state (those may return the same bool value for `operator true()` and `operator false()`). – Peter - Reinstate Monica Jan 15 '16 at 11:50
  • @PeterA.Schneider Correct, it's based on this statement `define only one of them directly and define the other one in terms of that, so that the definitions are always consistent` – Ivan Stoev Jan 15 '16 at 11:53
2

You can't call any operator methods explicitly in C#. operator true and operator false are no exception. It's just that most operator methods have a more straightforward method of invoking them implicitly.

If it makes sense to call the operator method in other scenarios than as an overload operator, provide it as a regular method instead. It's generally more readable and can nicely avoid the whole problem of having multiple separate implementations that you wanted to solve.

public class T
{
    private bool asBoolean() { ... } // or even make it public
    public static bool operator true(T t) { return t.asBoolean(); }
    public static bool operator false(T t) { return !t.asBoolean(); }
}

For completeness, you can implement operator false as e.g.

public static bool operator false(T t) { return t ? false : true; }

but please don't unless you absolutely need to.

  • The last suggestion is interesting. Why would you not do that? – Peter - Reinstate Monica Jan 15 '16 at 11:24
  • @PeterA.Schneider To me, it seems far less readable, especially when you start adding more operators to the class. I can't and don't want to stop you from doing it anyway of course. :) –  Jan 15 '16 at 11:28
  • Yes, especially when a conversion-to-bool op is introduced it will be unclear which code gets executed. But if all T are either true or false (which of course needs not be the case with the two operators), defining one in terms of the other seems to be logically sound from a maintenance perspective, especially if T (and hence the bool decision) may evolve over time. – Peter - Reinstate Monica Jan 15 '16 at 11:33
  • By the way, I was not aware of the fact that *none* of the operators can be called explicitly, the way they can in C++. Even casts are only triggers to call a conversion operator. In that light the fact that I cannot do it for `true()` and `false()` is much less surprising ;-). – Peter - Reinstate Monica Jan 15 '16 at 11:52