none
Overloading the == and != operators for an interface, impossible?

    Question

  • How do I overload the == and != operators for an interface declaration (that derives from Equatable<T>) so that equality behaves consistently and intelligently in all variations?

    To clarify, consider the following interface declaration:

            public interface IFish : IEquatable<IFish>
            {
                string Name { get; }
            }

    Suppose we now have a class that implements this interface.

            public class RedFish : IFish
            {
                   // impl snipped for post brevity, ask and I will post it...
            }

    How do I make the following unit test pass?

            [Test]
            public void OpOverloads_RedFish()
            {
                IFish fish1 = new RedFish("Mowgli");
                IFish fish2 = new RedFish("Mowgli");
                Assert.IsTrue((RedFish)fish1 == fish2); // no problem
                Assert.IsTrue(fish1 == (RedFish)fish2); // no problem
                Assert.IsTrue((RedFish)fish1 == (RedFish)fish2); // no problem
                Assert.IsTrue(fish1 == fish2); // fails here, not good since it will lead to very confusing bugs
            }

    As you can see, the problem boils down to that the C# compiler (seemingly?) won't let me define these overload signatures:

                public static bool operator ==(IFish fish1, IFish fish2) { ... }
                public static bool operator !=(IFish fish1, IFish fish2) { ... }

    I believe that if a type T defines Equals(T obj), then it should override ==(T obj1, T obj2) even if T is an interface. How do I do this??
    Tuesday, March 18, 2008 9:32 AM

Answers

  • As you can see, the problem boils down to that the C# compiler (seemingly?) won't let me define these overload signatures:

                public static bool operator ==(IFish fish1, IFish fish2) { ... }
                public static bool operator !=(IFish fish1, IFish fish2) { ... }

    I believe that if a type T defines Equals(T obj), then it should override ==(T obj1, T obj2) even if T is an interface. How do I do this??

     

    As already noted, you cannot implement operators on interfaces.  You gotta do it through the base class, which I would not declare as abstract in this case. 

     

    What would be the real difference, or loss, between casting to the base class instead of the interface?  The base class could inherit from IFish, instead of doing the inheritance with the classes derived from the base.  Just mark the methods as virtual, and make sure that you implement all overrides in the derived classes. 

     

    Rudedog

     

    Tuesday, March 18, 2008 1:44 PM
  • You can only implement public instance methods when implementing an interface.  Operators are static (class) methods and therefore can't be defined in an interface.

     

    Based on the questions about == and !=, I would suggest looking at the IEquatable interface.

     

    Tuesday, March 18, 2008 2:29 PM
  • In the C# language, Interface cannot contain operators. Based on my knowledge,It seems impossible to realize the == or != operations on your Interface reference.

     

    If you do wanna compare the two interface reference, implement the method Equals() in the IEquatable interface can solve your problem.

     

    Then you can use  Assert.IsTrue(fish1.Equals(fish2)); in your code. That is why you inherit IEquatable interface, why not use it.

     

    And about  "fish1==fish2" if you write code

     

    Code Snippet
    if (fish1 == fish2)
           System.Console.WriteLine("==succeed ");
      else
           System.Console.WriteLine("== false");

     

     

     

    And watch about the IL code using reflector.You can find

     

       L_003f: ldloc.0
        L_0040:
    ldloc.1
        L_0041:
    ceq       //compare the two value
        L_0043: ldc.i4.0
        L_0044: ceq
        L_0046: stloc.s CS$4$0000
        L_0048: ldloc.s CS$4$0000
        L_004a: brtrue.s L_0059
        L_004c: ldstr "==succeed "
        L_0051: call void [mscorlib]System.Console::WriteLine(string)
        L_0056: nop
        L_0057: br.s L_0064
        L_0059: ldstr "== false"
        L_005e: call void [mscorlib]System.Console::WriteLine(string)
        L_0063: nop

    As you can see from the IL code,It will compare the 2 reference address of fish1 and fish2, then it will always be false because they are referenced to 2 different object.

     

    Hope this can help you.

    Wishe.

    Zou

    Thursday, March 20, 2008 9:24 AM

All replies

  • As far as I can see from the C# standard, you can't overload operators in an Interface, only in a Class. To resolve your issue, you can create a BaseFish class to implement the IFish interface, then derive RedFish from the BaseFish. Overload the operators in BaseFish as needed.

     

     

     

     

    Tuesday, March 18, 2008 12:23 PM
  • You mean like this?

            public interface IFish : IEquatable<IFish>
            {
                string Name { get; }
            }

            public class FishBase : IFish
            {
                // ...
            }

            public class BlueFish : FishBase
            {
                // ...
            }


    This does not solve anything, the test still fails exactly like before. Try it, you'll see. The only way to make it work is to remove the IFish interface completely, let FishBase inherit IEquatable<FishBase> and declare all object references as FishBase. This works, but this is not what I want to do.

    But thank you for the answer anyway.
    Tuesday, March 18, 2008 12:35 PM
  •  

    You should still be able to use IFish and IEquatable<IFish> with FishBase, as well as declaring FishBase as generic and/or abstract.

     

    Rudedog

    Tuesday, March 18, 2008 12:49 PM
  •  Rudedog2 wrote:

     

    You should still be able to use IFish and IEquatable with FishBase, as well as declaring FishBase as generic and/or abstract.

     

    Rudedog

     

    Is this what you're looking for?

    Code Snippet

     

    public interface IFish

    {

    }

     

    abstract class FishBase<T> : IEquatable<T>

    where T : IFish

    {

    }

     

     

    So now you could do this....

     

    Code Snippet

    class BassFish : FishBase<IFish> , IFish

    {

    }

    class RedFish : FishBase<IFish>, IFish

    {

    }

     

     

    Hope this helps.

     

    Rudedog

    Tuesday, March 18, 2008 1:03 PM
  • Rudedog: Nope. Try it, it won't make my test pass.

    Yes, we can compare one FishBase to one IFish and make it work, but that design will suck. If somebody compares two IFish references we are back to square one.

    The only test that will pass is one where no two object references that are compared are declared as or casted to IFish, and that means that we have to remove IFish (or make it non public and conciously restict comparisons).

    Unless there is some other way to do this of course, if so please make a fool out of me. Wink
    Tuesday, March 18, 2008 1:11 PM
  • What were the results of these test?  True or false?

     

     [

    Code Snippet
    Test]
            public void OpOverloads_RedFish()
            {
                IFish fish1 = new RedFish("Mowgli");
                IFish fish2 = new RedFish("Mowgli");
                Assert.IsTrue((RedFish)fish1 == fish2); // no problem
                Assert.IsTrue(fish1 == (RedFish)fish2); // no problem
                Assert.IsTrue((RedFish)fish1 == (RedFish)fish2); // no problem
                Assert.IsTrue(fish1 == fish2); // fails here, not good since it will lead to very confusing bugs
            }

     

     

    Are we certain of your overloads?

     

    Rudedog

     

     

    Tuesday, March 18, 2008 1:30 PM
  • As you can see, the problem boils down to that the C# compiler (seemingly?) won't let me define these overload signatures:

                public static bool operator ==(IFish fish1, IFish fish2) { ... }
                public static bool operator !=(IFish fish1, IFish fish2) { ... }

    I believe that if a type T defines Equals(T obj), then it should override ==(T obj1, T obj2) even if T is an interface. How do I do this??

     

    As already noted, you cannot implement operators on interfaces.  You gotta do it through the base class, which I would not declare as abstract in this case. 

     

    What would be the real difference, or loss, between casting to the base class instead of the interface?  The base class could inherit from IFish, instead of doing the inheritance with the classes derived from the base.  Just mark the methods as virtual, and make sure that you implement all overrides in the derived classes. 

     

    Rudedog

     

    Tuesday, March 18, 2008 1:44 PM
  • You can only implement public instance methods when implementing an interface.  Operators are static (class) methods and therefore can't be defined in an interface.

     

    Based on the questions about == and !=, I would suggest looking at the IEquatable interface.

     

    Tuesday, March 18, 2008 2:29 PM
  • In the C# language, Interface cannot contain operators. Based on my knowledge,It seems impossible to realize the == or != operations on your Interface reference.

     

    If you do wanna compare the two interface reference, implement the method Equals() in the IEquatable interface can solve your problem.

     

    Then you can use  Assert.IsTrue(fish1.Equals(fish2)); in your code. That is why you inherit IEquatable interface, why not use it.

     

    And about  "fish1==fish2" if you write code

     

    Code Snippet
    if (fish1 == fish2)
           System.Console.WriteLine("==succeed ");
      else
           System.Console.WriteLine("== false");

     

     

     

    And watch about the IL code using reflector.You can find

     

       L_003f: ldloc.0
        L_0040:
    ldloc.1
        L_0041:
    ceq       //compare the two value
        L_0043: ldc.i4.0
        L_0044: ceq
        L_0046: stloc.s CS$4$0000
        L_0048: ldloc.s CS$4$0000
        L_004a: brtrue.s L_0059
        L_004c: ldstr "==succeed "
        L_0051: call void [mscorlib]System.Console::WriteLine(string)
        L_0056: nop
        L_0057: br.s L_0064
        L_0059: ldstr "== false"
        L_005e: call void [mscorlib]System.Console::WriteLine(string)
        L_0063: nop

    As you can see from the IL code,It will compare the 2 reference address of fish1 and fish2, then it will always be false because they are referenced to 2 different object.

     

    Hope this can help you.

    Wishe.

    Zou

    Thursday, March 20, 2008 9:24 AM