Home > Software design >  Does overloading ToString() prevent implicit comparison between numeric values?
Does overloading ToString() prevent implicit comparison between numeric values?

Time:12-06

I have an Outer class that contains a decimal field, called Value. It represents a value between 0 and 1 (inclusive) and contains a logical NOT ! operator that subtracts its value from 1.

public class Outer : IEquatable<decimal> {
    public decimal Value;

    public Outer(decimal val = 0.5m) => Value = val;

    public override string ToString() => $"Value: {Value}";
    public bool Equals(Outer o) => this.Value == o.Value;

    public static implicit operator decimal(Outer o) => o.Value;
    public static implicit operator Outer(decimal d) => new Outer(d);

    public static Outer operator !(Outer o) => new Outer(1m - Value);
}

I am attempting to compare equality (implicitly) between Outer a and Outer b using their Value fields. If I initialize a with 0.75m, !a should equal 0.25m.

public class TESTING_Outer {
    [NUnit.Framework.Test]
    public void Not_Operator_Negates_Value() {
        Outer a = 0.75m, b = 0.25m;

        Assert.AreEqual(0.25m, !a);      // works with direct value
        Assert.AreEqual(b, (decimal)!a); // works with explicit cast

        Assert.AreEqual(b, !a);          // fails with implicit cast
    }
}

As the comments state, this test fails when attempting to check the equality of two Outer instances, despite them having the same value.

I get the following error output message:

Not_Operator_Negates_Value (0.045s)
---
  Expected: <Value: 0.25>
   But was: <Value: 0.25>
---

Why does the last Assert fail, and how do I make sure my objects are comparing their values correctly?

CodePudding user response:

I actually stumbled upon my mistake while writing the rest of my code. Turns out I just forgot a couple of overloads.

Thank you to everyone who commented on my question. You were all quick to point out exactly what I was missing! :)

public class Outer : IEquatable<decimal> {
    public decimal Value;

    public Outer(decimal val = 0.5m) => Value = val;

    public override string ToString() => $"Value: {Value}";
    public bool Equals(Outer o) => this.Value == o.Value;

    public static implicit operator decimal(Outer o) => o.Value;
    public static implicit operator Outer(decimal d) => new Outer(d);

    public static Outer operator !(Outer o) => new Outer(1m - Value);

    // What I needed to add:
    public override bool Equals(object obj) =>
        obj as Outer == null ? false : (obj as Outer).Value == Value;
    public override int GetHashCode() => Value.GetHashCode();
}
  • Related