Implementing IEquatable<T> where T is an interface
Asked Answered
B

4

8

I have a domain model that is trivially represented by the following.

IMyInterface

ClassA : IMyInterface

ClassB : IMyInterface

What I want is to implement IEquatable in such a way that I can write code something like.

if(dynamicallyCreatedInstanceOfClassA == dynamicallyCreatedinstanceOfClassB)
{
  // Do something relevant
}

My first inclination was to have IMyInterface implement IEquatable but of course I can't actually do the implementing in IMyInterface. I would have to do that in ClassA and ClassB. What strikes me as wrong with this solution is that the implementations of IEquatable in ClassA and ClassB will be exactly, line for line the same. Is there an elegant way to accomplish this? When ClassC comes along I don't want to have to copy and past 50 lines of duplicated IEquatable code.

I have considered using an abstract base class instead of an interface, but in this scenario IMyInterface really just describes actions the class will perform and not what the class is. ClassA and ClassB don't share many similarities except they can both perform the actions in the IMyInterface contract.

Any insights are helpful, and thank you for your time.

Beveridge answered 30/9, 2009 at 15:18 Comment(3)
I would have suggested to go the abstract way as well, but the disadvantage is that that will limit your derived classes from inheriting from another concrete class as MI isn't allowed in C#. See supercat's answer to see the potential problem involving GetHashCode. Jon's approach is the only easy way out it seems..Beebe
@nawfal: Jon's approach is the right one, since it is entirely normal and expected for an IEqualityComparer<T> to represent rather broad forms of equivalence which T may know nothing about. For example, two objects that happen to implement IList<T> should probably only report themselves as equal to each other if they will always contain the same items in the same order. It may be useful, however, for an object to be able to store lists that it owns into a Dictionary<IList<T>,whaever>, where lists containing the same items in any sequence will compare identical.Hygiene
@nawfal: No such comparison method exists in IList<T>; implementations generally don't provide one either. Nonetheless, such a comparison method may be useful in some contexts, so it would be perfectly reasonable to use an outside class to implement an ICEqualityComparer<IList<T>> in such fashion.Hygiene
U
8

Instead of implementing IEquatable<T>, could you perhaps implement IEqualityComparer<T> in a separate class? In most cases where you're interested in equality you can specify a comparer instead, and I suspect it'll be cleaner that way.

Uracil answered 30/9, 2009 at 15:28 Comment(4)
That's a good idea Jon, thanks for the input. I've just about read through C# in Depth, and it has taught me a ton about the language I hadn't even considered before. I'll be reading it a second time immediately just to try to soak it all up.Beveridge
@Matthew: You might want to wait until the second edition is out for a second read - otherwise you'll need to read it all three times :)Uracil
You can get it on MEAP now (manning.com/skeet2) - the "new" chapters are basically done; I'm now revising the old ones. Should be out in hard copy some time H1 2010.Uracil
Awesome, I will have to check it out. And thanks again for the tip on IEqualityComparer<T> It worked out great.Beveridge
C
5

Based on your question it seems like you know what the Equals algorithm will be and that it will be the exactly the same for both ClassA and ClassB. Why not do the following

  1. Define IMyInterface to inherit from IEquatable<IMyInterface>
  2. Define an abstract base class MyInterfaceBase which implements IMyInterface and has the IEquatable<IMyInterface> implementation
  3. Have ClassA and ClassB derive from MyInterfaceBase
Candless answered 30/9, 2009 at 15:25 Comment(1)
I had considered this as well, I've been afraid that classA will need to inherit in the near future to achieve other goals, I can of course cross that bridge when I come to it, If I am going to go down this route I may as well just cut out the interface and use an abstract base class. Not out of the question either. Thanks for the input.Beveridge
H
1

If IEquatable<T> included a GetHashcode() member it might be possible to define meaningful semantics for that type which differed from Object.Equals (e.g. "if one were looking only at the characteristics which are present in T, would the objects be considered the same"). Because it does not, however, there generally isn't much reason to implement IEquatable<T> except in those cases where it can improve performance without adding any quirky semantics. In practice, that means that IEquatable<T> should either be implemented only by T itself (if T is a sealed class or struct), or not at all (if T is inheritable, or if the performance benefits aren't worth the effort).

Hygiene answered 6/12, 2012 at 20:13 Comment(0)
N
0

I would think that if the interface can't be converted to an abstract class, it doesn't make much sense to be comparing the two implemented classes to each other directly or automatically. Meaning that implementing a custom comparison on each class would be the way to go. I'm sure you can refactor the comparison code somehow to make changing it easier later on.

Nationalize answered 30/9, 2009 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.