Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Properly implement comparison of two objects with different type but semantically equivalent

I've found a similar question

How to compare two distinctly different objects with similar properties

that may implicitly and/or in part reply to my question.

Suppose I want compare (without a lot of nested conditions) this object:

class ObjectA {
  public string PropertyX { get; set; }
  public char PropertyY { get; set; }
  public long PropertyZ { get; set; }
}

to a System.String. I'm interested only in equality or inequality (not a range of values about identity).

Implementing IEquatable<string> in ObjectA is a proper choice? I don't care of what simply works, I want to identify the proper pattern for such case.

As other information, please consider that ObjectA will often be supplied as sequence of IEnumerable<ObjectA>.

I don't need to know if "string" == or != objectA instance; sorting is not involved.

Edit to clarify (and help)

Sorry but writing a good question is sometime difficult...

Suppose I can't represent ObjectA as string for the purpose of comparison (violating encapsulation is not an option).

  • In context-1 I've to match it against PropertyY.

  • In context-2 I've to match it against an algorithm applied to PropertyY/PropertyZ.

@Oliver solution in the end of the question helps me again (and +1 again). I can simply define a custom interface:

interface IContextConverter {
  string ToEquatableStringForContext1();
  string ToEquatableStringForContext2();  
}

Since I've also an ObjectB with same logic but different properties, both will implement IContextConverter (or maybe I find a better name) avoiding to violate RAP.

like image 295
jay Avatar asked Mar 07 '13 16:03

jay


People also ask

How do you compare two different objects?

Whereas the equals() method compares two objects. Objects are equal when they have the same state (usually comparing variables). Objects are identical when they share the class identity. For example, the expression obj1==obj2 tests the identity, not equality.

Is it safe to use the equality operators to compare reference types?

Most reference types must not overload the equality operator, even if they override Equals. However, if you are implementing a reference type that is intended to have value semantics, such as a complex number type, you must override the equality operator.

How do you equate two objects in JavaScript?

The equality operator (===) verifies whether the two operands are equal or not and returns a Boolean value. If the both operands are of different types, this operator return false else it returns true.


2 Answers

There are many possibilities.

If you feel an ObjectA is a kind of System.String, you could write a user-defined conversion (implicit or explicit) from ObjectA to System.String, or from System.String to ObjectA, or both directions.

You could also overload the == and != operators with a signature like operator ==(ObjectA oa, string s). Beware that there is difference between oa == s and s == oa.

Either of these two possibilities may lead to confusion. It would also be confusing to override the virtual Equals(object), or to introduce an overload Equals(string). Therefore I don't recommend implementing IEquatable<string>.

Why not simply write a method with an unused name, like public bool EqualsString(string s)? Then you will have to call this method explicitly, of course, but that will lead to less confusion. Another idea would be to use a constructor of signature public ObjectA(string s) and then implement "homogeneous" equality of ObjectA and ObjectA.

like image 20
Jeppe Stig Nielsen Avatar answered Oct 01 '22 23:10

Jeppe Stig Nielsen


I would strongly recommend to not implement IEquatable<string>, cause especially when working with collections, dictionaries, LINQ, etc. you don't really know when one of these methods will be called somewhere deep inside which leads maybe to subtle bugs.

Due to the fact that you like to compare two objects of different types a simple Comparer<T> wouldn't work also.

So either write a TypeConverter which converts your object into the desired type (in your case a string) or add a method to your object like .ToEquatableString() and use their output to compare your object with the other string.

Here is an example on you could get all elements, that match one of a string in another collection:

IEnumerable<String> otherElements = new[] {"abc", "def", "ghi" };
IEnumerable<ObjectA> myObjects = GetObjects();

var matchesFound = otherElements.Join( // Take the first collection.
              myObjects, // Take the second collection.
              s => s, // Use the elements in the first collection as key (the string).
              obj => obj.ToEquatableString(),  // Create a string from each object for comparison.
              (s, obj) => obj, // From the matching pairs take simply the objects found.
              StringComparer.OrdinalIgnoreCase); // Use a special string comparer if desired.
like image 78
Oliver Avatar answered Oct 01 '22 22:10

Oliver