Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit (bool) and == operator override - handle if statements correctly

I have a custom class with implement both the == and the implicit for boolean operator.

Is this the correct way to handle all possible, if ==/!= statements and get the expected result? Like this:

public class Foo
{
    public bool Result { get; set; }

    public static bool operator ==(bool @bool, Foo foo)
    {
        return Equals(foo, @bool);
    }
    public static bool operator !=(bool @bool, Foo foo)
    {
        return NotEquals(foo, @bool);
    }
    public static bool operator ==(Foo foo, bool @bool)
    {
        return Equals(foo, @bool);
    }
    public static bool operator !=(Foo foo, bool @bool)
    {
        return NotEquals(foo, @bool);
    }
    public static bool operator ==(Foo foo, Foo fooB)
    {
        return Equals(foo, fooB);
    }
    public static bool operator !=(Foo foo, Foo fooB)
    {
        return NotEquals(foo, fooB);
    }
    public static implicit operator bool(Foo foo)
    {
        try { return foo.Result; }
        catch { return false; }
    }

    private static bool Equals(Foo foo, Foo fooB)
    {
        if (object.Equals(foo, null))
        {
            if (object.Equals(fooB, null))
                return true;

            return false;
        }

        if (object.Equals(fooB, null))
            return false;

        return foo.Result == fooB.Result;
    }
    private static bool NotEquals(Foo foo, Foo fooB)
    {
        if (object.Equals(foo, null))
        {
            if (object.Equals(fooB, null))
                return false;

            return true;
        }

        if (object.Equals(fooB, null))
            return true;

        return fooB.Result != foo.Result;
    }
    private static bool Equals(Foo foo, bool @bool)
    {
        if (object.Equals(foo, null))
            return true;

        return @bool == foo.Result;
    }
    private static bool NotEquals(Foo foo, bool @bool)
    {
        if (object.Equals(foo, null))
            return false;

        return @bool != foo.Result;
    }

}

I am especially wondering about the fact that its seems you really need to implement overloads for either

if (new Foo() != true)

and

if (true != new Foo())
like image 249
Rand Random Avatar asked Jan 31 '14 16:01

Rand Random


2 Answers

I think you've written too much code :-)

The following is sufficient:

public class Foo
{
    public bool Result { get; set; }

    public static implicit operator bool(Foo foo)
    {
        return !object.ReferenceEquals(foo, null) && foo.Result;
    }
}

The compiler will then know how to implicitly convert variables of type Foo into bool. (And null will be converted to false).

So, when you write:

new Foo() == false

The compiler will use the implicit type converter to get a bool value from Foo and then use the standard equality operator for bool.

If we look at the IL that the compiler generates for that expression we find:

newobj instance void FooBool.Foo::.ctor()               // new Foo()
call bool FooBool.Foo::op_Implicit(class FooBool.Foo)   // implicit operator (Foo => bool)
ldc.i4.0                                                // false
ceq                                                     // equality operator (bool)

Here's a test:

static void Main(string[] args)
{
    AssertTrue(new Foo() == false);
    AssertTrue(false == new Foo());
    AssertFalse(new Foo() != false);
    AssertFalse(false != new Foo());
    AssertTrue(new Foo { Result = true } == true);
    AssertTrue(true == new Foo { Result = true });
    AssertFalse(new Foo { Result = true } != true);
    AssertFalse(true != new Foo { Result = true });
}

static void AssertTrue(bool value)
{
    Console.WriteLine(value ? "ok" : "not ok");
}

static void AssertFalse(bool value)
{
    Console.WriteLine(value ? "not ok" : "ok");
}

It prints ok for each test. So this simplified code should fulfill your needs if I understood them correctly.

UPDATE

To allow the equality operator to work for instances of Foo (which may be null):

public static bool operator ==(Foo a, Foo b)
{
    if (object.ReferenceEquals(a, b))
    {
        return true;
    }
    else if (object.ReferenceEquals(a, null))
    {
        return !b.Result;
    }
    else if (object.ReferenceEquals(b, null))
    {
        return !a.Result;
    }
    else
    {
        return a.Result == b.Result;
    }
}

You should then also implement the inequality operator:

public static bool operator !=(Foo a, Foo b)
{
    return !(a == b);
}

And also override GetHashCode + Equals

public override int GetHashCode()
{
    return this.Result ? 1 : 0;
}

public override bool Equals(object obj)
{
    if (object.ReferenceEquals(obj, null))
    {
        return !this.Result;
    }

    Type t = obj.GetType();

    if (t == typeof(Foo))
    {
        return this.Result == ((Foo)obj).Result;
    }
    else if (t == typeof(bool))
    {
        return this.Result == (bool)obj;
    }
    else
    {
        return false;
    }
}
like image 198
Mårten Wikström Avatar answered Nov 09 '22 12:11

Mårten Wikström


I think that you explicitly covered all the bases in your code; if needed you must consider the ordering of parameters for the operator so if you want your Equals function to be called for both ordering of parameters what you did is right.

However it looks a bit overkill in the case of comparing Foo to bool since you could simply rely on the implicit conversion. This would allow you to remove all operators between the two types as well as the Equals and NotEquals methods.

What's more, it would avoid some inconsistency in your code regarding the conversion of a null Foo to boolean. When you pass a null Foo to the Equals method it will return true whereas in the implicit conversion a null Foo will return false:

true == (Foo)null; //true
true == Convert.ToBoolean((Foo)null); //false

In closing, here is how i'd write the Foo class, i think it is sufficient:

public class Foo
{
    public bool Result { get; set; }

    public static bool operator ==(Foo foo, Foo fooB)
    {
        return Equals(foo, fooB);
    }
    public static bool operator !=(Foo foo, Foo fooB)
    {
        return NotEquals(foo, fooB);
    }
    public static implicit operator bool(Foo foo)
    {
        return foo == null ? false : foo.Result;
    }

    private static bool Equals(Foo foo, Foo fooB)
    {
        if (object.Equals(foo, null))
        {
            return object.Equals(fooB, null);
        }

        if (object.Equals(fooB, null))
            return false;

        return foo.Result == fooB.Result;
    }
    private static bool NotEquals(Foo foo, Foo fooB)
    {
        return !Equals(foo, fooB);
    }
}
like image 37
samy Avatar answered Nov 09 '22 12:11

samy