2013-07-12 2 views
9

Ну, я пытаюсь создать небольшое приложение, которое сохраняет некоторые имена сотрудников, возрасты, а также зарплаты. Поэтому я решил использовать Dictionary для того, чтобы установить зарплату каждого сотрудника и я пришел с этим кодомDictionary.ContainsKey Always Returns False

Код

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

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

Однако, как я закомментировать или название темы указано, .ContainsKey всегда возвращает мне False, хотя я пробовал оба способа, как показано в коде.

+1

Вы должны переопределить Equals и GetHashCode для вашего класса Employee –

+0

Я думаю, что это как-то связано с тем, как сравниваются объекты. Поскольку объекты в словаре и в 'employeeToFind' на самом деле являются разными объектами - их сравнение возвращает false –

ответ

18

Вы не используете конструктор Dictionary, который принимает IEqualityComparer<T>, и вы также не внедрили обычное равенство на Employee класс.

Так что сейчас словарь сравнивает сотрудников по ссылке. Когда вы работаете new, у вас есть другая ссылка, хотя, к примеру, имя может быть одинаковым.

Возможно, самым простым способом было бы реализовать собственный IEqualityComparer<Employee>, где вы могли бы выбрать, какие члены будут использоваться для сравнения равенства, и передать его конструктору словаря.

[EDIT] Как и было обещано, сниппеты:

//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; 
     } 
    } 
} 

И потом:

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! 

Для полноты, вот имя только Comparer (также любезно ReSharper в):

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); 
     } 
    } 

Но, как вы заметили, вам нужно решить, какой компас вы будете использовать для сравнения ключей при создании словаря. Это не может быть изменено позже ...

+0

Это хорошо, но можете ли вы сделать свой ответ более насыщенным, добавив фрагменты кода, как я мог бы или кто-либо другой, кто увидит этот вопрос, решить эта проблема в соответствии с кодами, которые я опубликовал? –

+0

@RuneS Конечно, дайте мне секунду, чтобы добавить в систему Visual Studio;) –

+0

@RuneS И сделано. –

3

Сотрудник является ссылочным типом. Ключ словаря при добавлении нового сотрудника будет содержать ссылочный адрес этого объекта Employee. Если вы создаете другой объект Employee, он имеет ссылку на различия, чем ваш первый объект Employee, хотя они содержат одни и те же данные.