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())
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;
}
}
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With