Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# How to find if two objects are equal

I want to know the best way to compare two objects and to find out if they're equal. I'm overriding both GethashCode and Equals. So a basic class looks like:

public class Test
{
    public int Value { get; set; }
    public string String1 { get; set; }
    public string String2 { get; set; }

    public override int GetHashCode()
    {
        return Value ^ String1.GetHashCode() ^ String2.GetHashCode();
    }

    public override bool Equals( object obj )
    {
        return GetHashCode() == obj.GetHashCode();
    }
}

So for testing purposes I created two objects:

Test t = new Test()
{
    Value = 1,
    String1 ="One",
    String2 = "One"
};

Test t2 = new Test()
{
    Value = 1,
    String1 = "Two",
    String2 = "Two"
};

bool areEqual = t.Equals( t2 );

In testing this areEqual returns true event though both objects are different. I realise this is because String1 and String2 are the same value in each object and thus cancels each other out when hashing.

Is there a better way off hashing object that the method I have that will resolve my issue?

like image 291
lancscoder Avatar asked May 27 '10 10:05

lancscoder


3 Answers

Your current equality method is broken - there are more values than possible hash codes. It's entirely reasonable (and expected) that you will occasionally have values which are unequal but give the same hash. Equals should check the actual values:

public override bool Equals(object obj)
{
    Test test = obj as Test;
    if (obj == null)
    {
        return false;
    }
    return Value == test.Value &&
        String1 == test.String1 &&
        String2 == test.String2;
}

A few things to note:

  • Your way of generating the hashcode will give the same value for any fixed Value if String1 and String2 are the same; it will also blow up if String1 or String2 is null. This is an unfortunate aspect of using XOR for hashing. I prefer something like this:

    // Put this extension method in a utility class somewhere
    public static int SafeGetHashCode<T>(this T value) where T : class
    {
        return value == null ? 0 : value.GetHashCode();
    }
    
    // and this in your actual class
    public override int GetHashCode()
    {
        int hash = 19;
        hash = hash * 31 + Value;
        hash = hash * 31 + String1.SafeGetHashCode();
        hash = hash * 31 + String2.SafeGetHashCode();
        return hash;
    }
    
  • Generally speaking, equality becomes tricky when inheritance gets involved. You may want to consider sealing your class.

  • You may also want to implement IEquatable<Test>

like image 183
Jon Skeet Avatar answered Nov 16 '22 01:11

Jon Skeet


Your Equals is incorrect - that should define what it means for two things to be equal - and having the same hash-code does not mean equality (however; a different hash-code does mean non-equality). If "equality" means "both strings are pairwise equal", then test that.

Re a better hash; xor is notorious for this, since it is trivial to get 0 by xor a value with itself. A better approach may be something like:

int i = 0x65407627;
i = (i * -1521134295) + Value.GetHashCode();
i = (i * -1521134295) + (String1 == null ? 0 : String1.GetHashCode());
i = (i * -1521134295) + (String2 == null ? 0 : String2.GetHashCode());
return i;
like image 28
Marc Gravell Avatar answered Nov 16 '22 00:11

Marc Gravell


simple

Object.Equals(obj1, obj2);
like image 6
Manish G. Bhoraniya Avatar answered Nov 16 '22 01:11

Manish G. Bhoraniya