9

Well i am trying to create a small application that saves some employees Names, Ages as well as salaries. So i decided to use Dictionary in order to set every employee's salary and i came up with that code

Code

var employeeSalaryDictionary = new Dictionary<Employee, int>();
employeeSalaryDictionary.Add(new Employee { Name = "Chuck", Age = 37 }, 1000);
employeeSalaryDictionary.Add(new Employee { Name = "Norris", Age = 37 }, 2000);
employeeSalaryDictionary.Add(new Employee { Name = "Rocks", Age = 44 }, 3000);

Employee employeeToFind = new Employee { Name = "Chuck", Age = 37 };
//or even
Employee employeeToFind = new Employee { Name = "Chuck"};

//Always False...
bool exists = employeeSalaryDictionary.ContainsKey(employeeToFind); 

Employee Class

public class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
}

However as i commented out or as the topic title states, .ContainsKey always returns me False although i tried both ways as shown in the code.

Roman Ratskey
  • 5,101
  • 8
  • 44
  • 67

2 Answers2

18

You're not using the Dictionary constructor that takes in the IEqualityComparer<T> and neither have you implemented custom equality on the Employee class.

So right now the dictionary is comparing employees by reference. When you new an employee, you have a different reference, even though e.g. name might be the same.

Probably the easiest way here would be to implement your own IEqualityComparer<Employee>, where you could pick which members will be used for equality comparison, and pass it to the dictionary's constructor.

[EDIT] As promised, snippets:

//ReSharper's courtesy
public sealed class NameAgeEqualityComparer : IEqualityComparer<Employee>
{
    public bool Equals(Employee x, Employee y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(x, null)) return false;
        if (ReferenceEquals(y, null)) return false;
        if (x.GetType() != y.GetType()) return false;
        return string.Equals(x.Name, y.Name) && x.Age == y.Age;
    }

    public int GetHashCode(Employee obj)
    {
        unchecked
        {
            return ((obj.Name != null ? obj.Name.GetHashCode() : 0) * 397) ^ obj.Age;
        }
    }
}

And then:

var employeeSalaryDictionary = new Dictionary<Employee, int>(new NameAgeEqualityComparer());
employeeSalaryDictionary.Add(new Employee { Name = "Chuck", Age = 37 }, 1000);
employeeSalaryDictionary.Add(new Employee { Name = "Norris", Age = 37 }, 2000);
employeeSalaryDictionary.Add(new Employee { Name = "Rocks", Age = 44 }, 3000);

Employee employeeToFind = new Employee { Name = "Chuck", Age = 37 };
bool exists = employeeSalaryDictionary.ContainsKey(employeeToFind); // true!

For completeness, here's name-only comparer (also ReSharper's courtesy):

 public sealed class NameEqualityComparer : IEqualityComparer<Employee>
 {
        public bool Equals(Employee x, Employee y)
        {
            if (ReferenceEquals(x, y)) return true;
            if (ReferenceEquals(x, null)) return false;
            if (ReferenceEquals(y, null)) return false;
            if (x.GetType() != y.GetType()) return false;
            return string.Equals(x.Name, y.Name);
        }

        public int GetHashCode(Employee obj)
        {
            return (obj.Name != null ? obj.Name.GetHashCode() : 0);
        }
  }

But, as you noticed, you have to decide which comparer you will use for key-comparison when you create a dictionary. That can't be changed later on...

Patryk Ćwiek
  • 14,078
  • 3
  • 55
  • 76
  • That is nice but can you make your answer more rich by adding code snippets of how could i or any one else who will see that question solve this problem according to the codes i have posted ? – Roman Ratskey Jul 12 '13 at 21:56
  • @RuneS Sure, give me a sec to spool up Visual Studio ;) – Patryk Ćwiek Jul 12 '13 at 21:56
  • Could you explain why you added the `* 397`? – Alex Filipovici Jul 12 '13 at 22:04
  • 2
    @AlexFilipovici That's generated by ReSharper, potential reasons are [here](http://stackoverflow.com/a/102878/1180426). Other good hash-code implementation, that's also easier to remember than this one, is the one from 'Effective Java' - I've seen Jon Skeet using it here and there... – Patryk Ćwiek Jul 12 '13 at 22:06
  • @PatrykCwiek : That is truly epic! thank you very much for your very nice, detailed help... – Roman Ratskey Jul 12 '13 at 22:11
4

Employee is a reference type. Your dictionary key when you add a new employee is going to contain the reference address to that Employee object. If you create another Employee object it has a difference reference than your first Employee object, even though they contain the same data

jamesSampica
  • 12,230
  • 3
  • 63
  • 85