22

my class:

public class myClass
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }
    public int D { get; set; }
}

and main example:

Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>();
myClass first = new myClass();
first.A = 2;
first.B = 3;

myClass second = new myClass();
second.A = 2;
second.B = 3;
second.C = 5;
second.D = 6;

dict.Add(first, new List<string>());

if (dict.ContainsKey(second))
{
    //
    //should come here and update List<string> for first (and only in this example) key 
    //
}
else
{
    //
    //if myFirst object has difference vlues of A or B properties
    //
    dict.Add(second, new List<string>());
}

How to do this?

assylias
  • 321,522
  • 82
  • 660
  • 783
Saint
  • 5,397
  • 22
  • 63
  • 107

3 Answers3

37

If you always want the dictionary only to compare on A and B, you have two options. Either use the constructor that implements IEqualityComparer<TKey> and put your comparison logic there, or have your class implement IEquateable<T> GetHashCode and Equals so the default comparer will give you the results you are looking for.

If you only want to compare on A and B in your one situation you will need to use the .Keys property and the Linq extension method Contains that allows you to pass in a IEqualityComparer<T>. However, when doing it this way you loose the speed benefits of using a Dictionary, so use it sparingly.

public class MyClassSpecialComparer : IEqualityComparer<myClass>
{
    public bool Equals (myClass x, myClass y)
    { 
        return x.A == y.A && x.B == y.B 
    }

    public int GetHashCode(myClass x)
    {
       return x.A.GetHashCode() + x.B.GetHashCode();
    }


}


 //Special case for when you only want it to compare this one time
 //NOTE: This will be much slower than a normal lookup.
    var myClassSpecialComparer = new MyClassSpecialComparer();
    Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>();
    //(Snip)
    if (dict.Keys.Contains(second, myClassSpecialComparer ))
    {
        //
        //should come here and update List<string> for first (and only in this example) key 
        //
    }

 //If you want it to always compare
    Dictionary<myClass, List<string>> dict = new Dictionary<myClass, List<string>>(new MyClassSpecialComparer());
Richard
  • 25,390
  • 3
  • 25
  • 38
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
13

By default, comparison puts objects into buckets based on their hash code. A detailed comparison is then performed (by calling Equals) if two hash codes are the same. If your class neither provides GetHashCode or implements equality, the default object.GetHashCode will be used--in which case nothing specific to your class will be used for value comparison semantics. Only the same reference will be found. If you don't want this, implement GetHashCode and implement equality.

For example:

public class myClass
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }
    public int D { get; set; }

    public bool Equals(myClass other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return other.A == A && other.B == B && other.C == C && other.D == D;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (myClass)) return false;
        return Equals((myClass) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int result = A;
            result = (result*397) ^ B;
            result = (result*397) ^ C;
            result = (result*397) ^ D;
            return result;
        }
    }
}
Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
  • 2
    @AlexanderEfimov That makes it so the code is order dependent if you just XORed all of the items `A = 1, B = 2` would generate the same hash `3` has `A = 2, B = 1`. However if you multiply the second item by 397 it gives you `A = 1, B = 2` generates` 795`, but `A = 2, B = 1` generates `399`. This gives you a better hash distribution (one note, you should add not xor and initialize the first number with another prime) – Scott Chamberlain Jul 19 '12 at 14:57
  • 2
    See [this SO question and answer](http://stackoverflow.com/questions/892618/create-a-hashcode-of-two-numbers) for more of a explanation. – Scott Chamberlain Jul 19 '12 at 14:59
4

Override in your myClass:

  • GetHashCode method

  • Equals method

To implement GetHashCode method you can just XOR GetHashCodes from your integer properties.

Optionally override ToString method and implement IEquatable interface

Regfor
  • 8,515
  • 1
  • 38
  • 51