c# How to find if two objects are equal

37,241

Solution 1

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>

Solution 2

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;

Solution 3

simple

Object.Equals(obj1, obj2);

Solution 4

You could serialise the two objects to JSON, then compare the two strings to see if they are the same.

For example

JavaSriptSerializer serialiser = new JavaScriptSerializer();

string t1String = serialiser.Serialize(t);
string t2String = serialiser.Serialize(t2);

if(t1String == t2String)
   return true; //they are equal
else
   return false;

Solution 5

For any two objects, object equality implies hash code equality, however, hash code equality does not imply object equality. From Object.GetHashCode on MSDN:

A hash function must have the following properties:

If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.

In other words, your Equals is written wrong. It should be something like:

public override bool Equals(object obj)
{
    Test other = obj as Test;
    if (other == null)
        return false;

    return (Value == other.Value)
        && (String1 == other.String1)
        && (String2 == other.String2);
}

GetHashCode is good for collections (like Dictionary<K, V>) to quickly determine approximate equality. Equals is for comparing if two objects really are the same.

Share:
37,241
lancscoder
Author by

lancscoder

I’m a computer programmer from Lancashire working in London specialising in HTML, JavaScript, CSS and ASP.NET.

Updated on November 02, 2020

Comments

  • lancscoder
    lancscoder over 3 years

    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?

  • Marc Gravell
    Marc Gravell almost 14 years
    (updated, borrowing the seed and multiplier that the C# compiler uses for anonymous types)
  • Marc Gravell
    Marc Gravell almost 14 years
    Re SafeGetHashCode - just to mention that EqualityComparer<T>.GetHashCode will do something very similar - the extension method is quite tidy, though ;p
  • CARLOS LOTH
    CARLOS LOTH almost 14 years
    Why did you choose those values for generating the hash code?
  • Jon Skeet
    Jon Skeet almost 14 years
    @Carlos: See Marc's previous comment :)
  • OlimilOops
    OlimilOops almost 14 years
    Your answer is really good, whereas the first word is wrong: "No" is the wrong answer to his question: "Is there a better way ..." Moreover Simons Test class does not have a base class nor does it implement a interface. so the override keyword is really wrong
  • Jon Skeet
    Jon Skeet almost 14 years
    @Oops: Will fix the "No"... but the Test class does have a base class - the implicit base class of System.Object. These methods are overriding the Object implementations.
  • Jon Skeet
    Jon Skeet almost 14 years
    You should override the method from Object. You could also implement IEquatable<Test>, which is when you'd specify this signature.
  • OlimilOops
    OlimilOops almost 14 years
    hmm don't know, is it an issue of the VS2010 beta? If I use override here, VS says: 'Test.Equals(Test)': no suitable method found to override
  • CARLOS LOTH
    CARLOS LOTH almost 14 years
    @Jon: I've seen if before, but I haven't understood. Thanks for trying to help.
  • Marc Gravell
    Marc Gravell almost 14 years
    @Carlos - I honestly don't know why MS chose those ones specifically - the seed+factor approach is common enough, though. The values can be pretty much anything that gives a decent scatter (and the -ve may help with that)
  • Lee Taylor
    Lee Taylor over 11 years
    This all depends on whether obj1/obj2 are reference types or not. See msdn.microsoft.com/en-us/library/bsc2ak47.aspx
  • liang
    liang over 10 years
    it doesn't always work. JSON has its own limitation. Depending on JSON to compare general object is not a good idea.
  • Aviad P.
    Aviad P. about 10 years
    This is actually the best answer, it's the briefest, clearest and 100% correct.
  • NickG
    NickG about 8 years
    Tried this and it doesn't work. It causes false negatives due to the different ordering of properties in the JSON.