1

Suppose I have an interface IFoo, and a class Foo that implements IFoo. I want to define my own logic for determining if 2 instances of Foo are equal based on the data they contain and overload the == & != operators for ease of use when working with Foo.

If I have 2 instances of Foo, both stored as Foo, then this works fine. However, if I have 2 instances both stored as IFoo, suddenly my overloaded operators aren't being called anymore. Furthermore, since I can't define an operator on my IFoo interface and at least one of the arguments in my Foo operator overload must be of type Foo, I can't see that there's any way I can successfully overload the operator on my type. Is this correct?

Also, can anyone clear up why this is happening? Normally I'd expect that the fact my Foos are being stored as IFoos should be irrelevant in determining which equality function gets called, since fundamentally they're still Foos!

Any help really appreciated

Thanks

Edit: Ok, since there seems to be a bit of confusion of what I mean, here's an example which should hopefully clarify a bit:

public interface IFoo
{
}

public class Foo : IFoo
{
    public static bool operator==(Foo left, Foo right)
    {
        ....
    }
}

Foo foo1 = new Foo();
Foo foo2 = new Foo();
bool comparison1 = foo1 == foo2    //This is successfully calling the overloaded operator

IFoo ifoo1 = foo1;
IFoo ifoo2 = foo2;
bool comparison2 = ifoo1 == ifoo2    //This isn't
6
  • 1
    Operators are resolved only based on compile-time type. Commented May 25, 2018 at 18:10
  • What do you mean "stored as Foo"? Are you talking about serialization? Commented May 25, 2018 at 18:10
  • Unless something has changed, I don't think interfaces can have overloaded operators (since those are static). The only workarounds I've seen are to use methods instead of operators. For example, myFoo.Add(otherFoo) instead of myFoo + otherFoo Commented May 25, 2018 at 18:13
  • @David - he already says that "since I can't define an operator on my IFoo interface". Commented May 25, 2018 at 18:15
  • 1
    @David - totally agree. There isn't any other way and the best way is methods - which would be consistent with things like the Equals method for strings. I think his main confusion is not understanding why an object declared as an instance of IFoo, even if it is instantiated as Foo, cannot resolve the reference to the overloaded == operator. Where if the declaration and instantiation were both Foo, it could. Commented May 25, 2018 at 18:21

2 Answers 2

3

Operator overloading exists in compile-time only, after compilation it is just a call to a specific static method with well-known name.

C# compiler recognizes operator overloading for Foo and replaces a == b with Foo.op_Equality(a,b). That means there is non-virtual static method op_Equality in class Foo after compilation. Therefore it is not possible to overload operators for interfaces - interfaces cannot have static methods. Moreover, operator methods are non virtual, that means actual method is resolved from types of variables, not types of values, so compiler will look for possible operator overloading based on variable types of a and b, if both of them are IFoo, then it can only look for op_Equality in IFoo type - which cannot have it.

You can provide custom logic only for methods which are resolved based on types of values - those are virtual methods, e.g. you can override Equals method for Foo type, but it won't help you with IFoo a == IFoo b, which will be resolved to Object.ReferenceEquals(a,b)

Sign up to request clarification or add additional context in comments.

3 Comments

Ok, that makes sense and I did suspect what you described was happening, I just didn't know exactly why. So I guess this means there's no way to overload an operator on a class that implements an interface and have any kind of reliability that it will actually be used. Annoying, but good to know, otherwise I suspect that could have been the source of some incredibly sneaky bugs
@JCoyle There's no unreliability there. If you're comparing objects of type Foo it'll use that comparison. If you compare objects of some other type, it won't. It's very predictable.
@Servy yes, I know it's predictable in the sense that it's not randomly choosing equality methods, but it does mean from the perspective of consuming the class that behaviour of Foo will vary based on how it's being stored, meaning I could very easily accidentally check reference equality when I want to check value equality, so overloading the operator does mean a less reliable interface.
2

Basically what Aloraman said, but let me put it another way to see if it gets clearer.

When you have two variables declared as IFoo, but then you instantiate them as Foo objects, the variable declaration defines what is available at the syntax check. The variable declaration is there for the compiler to see, but the instantiation, which does not occur until execution, is not, so the compiler has no way to see that those are really two Foo objects. Because IFoo does not have, and cannot have an overloaded operator ==, the compiler rightly sees that as an error.

When the compiler tries to figure out what to use for some operation, it cannot know that those objects are really Foo, because assigning those IFoo variables to contain Foo objects happens during execution. The compiler cannot read your mind that you really mean to use the one class that implements IFoo and overloads == when you compare those twoo IFoo variables.

But when the variables are defined as Foo, the compiler knows perfectly well that at execution, when they get instantiated, they will have to be instantiated as Foo type, and it knows exactly where to go to compile that == operator.

Basically, the compiler, when it tries to compile that == operator, does NOT go over all your other code to see how you instantiated the two operands. If they are defined as IFoo, that is the only information it can have.

1 Comment

Thanks for this, between the answers from you and Aloraman this makes much more sense now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.