I inherited some code where Equals was overridden but not ==. I fixed that up, but I noticed more problems. In the calling code objects are constructed using interfaces and then compared and the results are not what one would expect. I've put a complete minimal example below. Briefly, given ISilly i1 = new Silly() and ISilly i2 = new Silly, i1==i2 return false, not true. Is there a way to fix this up and should I? I saw some arguments along the lines of "i1 and i2 are interaces. There could be many classes that derive from ISilly so asking if i1 and i2 doesn't make sense except in a reference equality sense". If that's the answer, my two questions would be:
- Why does Equals work so beautifully?
- How do I stop the application programmer from calling "i1 == i2"? It more natural for him/her to write that than i1.Equals(i2).
I tried putting in public static bool operator ==(ISilly s1, ISilly s2)- notice the interface instead of class name. I get compiler error.
What am I missing here? I have tried searching for an article that addresses this with no luck. I would have thought it is a common problem.
Please let me know if anything is unclear or I can provide more information. Thanks, Dave
UPDATE !!! I just found this related question: Operator Overloading with Interface-Based Programming in C# As I read it, the answer seems to state that you just can't use == with interfaces and one of the answers suggests using a 3rd party tool such as Resharper to disallow it. Given what nasty bugs it can produce, I question the usefulness of == at all. Why even allow it?
namespace EqualityProblems
{
class Program
{
static void Main(string[] args)
{
// just use class, not interface!
Silly s1 = new Silly();
Silly s2 = new Silly();
Silly s3 = new Silly(42);
Silly s4 = null;
Silly s5 = null;
Console.WriteLine("s1.Equals(s2) should be true " + s1.Equals(s2)); // should be true
Console.WriteLine("s1.Equals(s3) should be false " + s1.Equals(s3)); // should be false
Console.WriteLine("s1.Equals(s4) should be false " + s1.Equals(s4)); // should be false
Console.WriteLine("s1 == s2 should be true " + (s1 == s2)); // should be true
Console.WriteLine("s1 != s2 should be false " + (s1 != s2)); // should be false
Console.WriteLine("s1 == s3 should be false " + (s1 == s3)); // should be false
Console.WriteLine("s4 == s1 should be false " + (s4 == s1)); // should be false
Console.WriteLine("s1 == s4 should be false " + (s1 == s4)); // should be false
Console.WriteLine("s4 == s5 should be true " + (s4 == s5)); // should be true;both are null
//Console.WriteLine("s4.Equals(s1) should crash " + s4.Equals(s1)); // should crash. s4 is null
ISilly i1 = new Silly();
ISilly i2 = new Silly();
ISilly i3 = new Silly(42);
ISilly i4 = null;
ISilly i5 = null;
Console.WriteLine("i1.Equals(i2) should be true " + i1.Equals(i2)); // should be true
Console.WriteLine("i1.Equals(i3) should be false " + i1.Equals(i3)); // should be false
Console.WriteLine("i1.Equals(i4) should be false " + i1.Equals(i4)); // should be false
Console.WriteLine("i1 == i2 should be true " + (i1 == i2)); // should be true BUT IS FALSE
Console.WriteLine("i1 != i2 should be false " + (i1 != i2)); // should be false BUT IS TRUE
Console.WriteLine("i1 == i3 should be false " + (i1 == i3)); // should be false
Console.WriteLine("i4 == i1 should be false " + (i4 == i1)); // should be false
Console.WriteLine("i1 == i4 should be false " + (i1 == i4)); // should be false
Console.WriteLine("i4 == i5 should be true " + (i4 == i5)); // should be true;both are null
//Console.WriteLine("i4.Equals(i1) should crash " + i4.Equals(i1)); // should crash. i4 is null
}
}
public interface ISilly
{
int Length { get; set; }
}
public class Silly : ISilly
{
public Silly(int n) { Length = n; }
public Silly() { Length = 7; }
public int Length { get; set; }
public override bool Equals(object obj)
{
return obj is ISilly sl && (sl.Length == Length);
}
public bool Equals(Silly other)
{
if (other == null) return false;
return Length == other.Length;
}
public override int GetHashCode()
{
return Length;
}
public static bool operator ==(Silly s1, Silly s2)
{
if (ReferenceEquals(s1, null))
{
return ReferenceEquals(s2, null) ? true : false;
}
return s1.Equals(s2);
}
public static bool operator !=(Silly fs1, Silly fs2)
{
return !fs1.Equals(fs2);
}
}
}