By jay


2013-03-07 16:02:05 8 Comments

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.

3 comments

@Jeppe Stig Nielsen 2013-03-07 16:40:13

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.

@jay 2013-03-07 17:08:45

+1 @Jeppe Stig Neilsen, liked the ObjectA::EqualsString(string) solution. It leads to code that it's simple and clear to read (and as more I learn, this is my priority).

@Pieter Geerkens 2013-03-07 16:14:51

NOTE: I do not particulary recommend this solution for this particular case, but I have often used this framework to implement Value Equality for structs. Difficult trade-offs are common in our field, and answers that address those, accompanied by appropriate caveats, seem in order.

I take it that you wish to design Value Equality semamtics on your class in the same fashion as the .NET framework does this for the string class. At a minimum, the following is necessary:

public override bool Equals(object obj) { 
  return (obj is ObjectA) && this == (ObjectA)obj; 
}
bool IEquatable<ObjectA>.Equals(ObjectA rhs) { 
  return this == rhs; 
}
public static bool operator != (ObjectA lhs, ObjectA rhs) { 
  return ! (lhs == rhs); 
}
public static bool operator == (ObjectA lhs, ObjectA rhs) {
  return (lhs.PropertyX == rhs.PropertyX);
}
public override int GetHashCode() { 
  return PropertyX.GetHashCode() 
}

Expanding to allow Value comparisons beween ObjectA and string:

bool IEquatable<ObjectA>.Equals(string rhs) { 
  return this == rhs; 
}
public static bool operator != (ObjectA lhs, string rhs) { 
  return ! (lhs == rhs); 
}
public static bool operator != (string lhs, ObjectA rhs) { 
  return ! (lhs == rhs); 
}
public static bool operator == (ObjectA lhs, string rhs) {
  return (lhs.PropertyX == rhs);
}
public static bool operator == (string lhs, ObjectA rhs) {
  return (lhs == rhs.PropertyX);
}

@jay 2013-03-07 16:19:35

+1 for sample code. But still in doubt that this is the solution. My primary need is to search an IEnumerable<ObjectA> using System.String without moving comparison logic to the Linq query.

@Pieter Geerkens 2013-03-07 16:26:24

@jay: see update. You may encounter difficulties if the framework attempts to perform string.Equals(ObjectA) instead of ObjectA.Equals(string). I do not recommend this, as it is likely dependent on version specific implementation of the framework.

@jay 2013-03-07 16:28:15

this explains better you solution. Appreciated.

@Oliver 2013-03-07 16:15:14

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.

@jay 2013-03-07 16:17:15

+1, @Oliver, ToEquatableString() returns a wrapper of ObjectA that implements IEquatable<string>?

@Oliver 2013-03-07 16:22:57

@jay: No. It returns a string that represents objectA. So essentially the same like .ToString() does. This string can than be used like any other string for comparison.

@YavgenyP 2013-03-07 16:23:57

+1, wanted to write the same. While this interface is provided by the framework, i think in 99.9999% of the cases its a very bad idea to use it. With all the examples ive seen so far using it (or similar concepts), its done nothing good, but created an unreadable and non - clear code, instead of a simple ObjectA.PropertyX == "string", which can be understood by even someone who wrote his first "Hello world!" a day before.

@jay 2013-03-07 16:25:16

@Oliver, it seems the more simple solution; but also TypeConverter solution seems clean. This answer my question.

@jay 2013-03-09 07:56:57

@Oliver, last editing clarify and presents a clean implementation. Sorry I can't +1 again...

Related Questions

Sponsored Content

26 Answered Questions

[SOLVED] Why not inherit from List&lt;T&gt;?

13 Answered Questions

[SOLVED] How to determine if a type implements an interface with C# reflection

41 Answered Questions

[SOLVED] How do I properly clean up Excel interop objects?

1 Answered Questions

14 Answered Questions

[SOLVED] Getting all types that implement an interface

12 Answered Questions

[SOLVED] How to create a new object instance from a Type

13 Answered Questions

[SOLVED] Programmatic equivalent of default(Type)

9 Answered Questions

[SOLVED] Difference between InvariantCulture and Ordinal string comparison

15 Answered Questions

[SOLVED] Calculate difference between two dates (number of days)?

  • 2009-10-22 13:47:15
  • leora
  • 1088725 View
  • 1027 Score
  • 15 Answer
  • Tags:   c# date

Sponsored Content