views:

108

answers:

3

Let's say I have a POCO:

public class Person
{
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public IList<Person> Relatives { get; set; }
}

I want to compare two instances of Person to see if they're equal to each other. Naturally, I would compare Name, DateOfBirth, and the Relatives collection to see if they're equal. However, this would involve me overriding Equals() for each POCO and manually writing the comparison for each field.

My question is, how can I write a generic version of this so I don't have to do it for each POCO?

+1  A: 

It is possible to use reflection to do this in a general way, but it has performance and complexity drawbacks. It's much better to implement Equals and GetHashCode manually so that you get the results you expect.

See Should I Overload == Operator?

GraemeF
A: 

If you are not worried about performance, you could use reflection in a utility function to iterate over each field and compare their values.

using System; 
using System.Reflection; 


public static class ObjectHelper<t> 
{ 
    public static int Compare(T x, T y) 
    { 
     Type type = typeof(T); 
     var publicBinding = BindingFlags.DeclaredOnly | BindingFlags.Public;
     PropertyInfo[] properties = type.GetProperties(publicBinding); 
     FieldInfo[] fields = type.GetFields(publicBinding); 
     int compareValue = 0; 


     foreach (PropertyInfo property in properties) 
     { 
      IComparable valx = property.GetValue(x, null) as IComparable; 
      if (valx == null) 
       continue; 
      object valy = property.GetValue(y, null); 
      compareValue = valx.CompareTo(valy); 
      if (compareValue != 0) 
       return compareValue; 
     } 
     foreach (FieldInfo field in fields) 
     { 
      IComparable valx = field.GetValue(x) as IComparable; 
      if (valx == null) 
       continue; 
      object valy = field.GetValue(y); 
      compareValue = valx.CompareTo(valy); 
      if (compareValue != 0) 
       return compareValue; 
     } 
    return compareValue; 
    } 
}
Nescio
Thanks, I'll try your code out.
Daniel T.
Hmm, I gave it a try,, but the `properties` and `fields` arrays are empty.
Daniel T.
Found out why. Remove the publicBinding variable, or use some a different enum type than DeclaredOnly or Public.
Daniel T.
The BindingFlags allow you to access any combination of fields, public or private or static, etc. I am sorry my defaults were not valid for you, but I'm glad you figured it out!
Nescio
A: 

Implementing Equals() and GetHashCode() isn't much hassle.

public override bool Equals(object obj)
{
    if (ReferenceEquals(this, obj) return true;
    if (!(obj is Person)) return false;

    var other = (Person) obj;
    return this == other;
}

public override int GetHashCode()
{
    return base.GetHashCode();
}

See Using Equals/GetHashCode Effectively

James Simm
I'm confused. Your example is simply the default behavior of Equals(). And it may not be much hassle for one class but when you need to add in a bunch of boilerplate code to 50+ classes, it does become a hassle.
Daniel T.