2011-01-24 2 views
7

, так что я пытаюсь сделать эту работу, и я не могу понять, почему он не работает& LINQ отчетливые, реализация равных & GetHashCode

демонстрационного кода;

namespace ConsoleApplication1 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var myVar = new List<parent >(); 
     myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } }); 
     myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } }); 

     var test = myVar.Distinct(); 

     Console.ReadKey(); 

    } 
} 


public class parent : IEquatable<parent> 
{ 
    public String id { get;set;} 
    public String blah1 { get; set; } 
    public child c1 { get; set; } 

    public override int GetHashCode() 
    { 
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      // Suitable nullity checks etc, of course :) 
      hash = hash * 23 + id.GetHashCode(); 
      hash = hash * 23 + blah1.GetHashCode(); 
      hash = hash * 23 + (c1 == null ? 0 : c1.GetHashCode()); 
      return hash; 
     } 
    } 

    public bool Equals(parent other) 
    { 
     return object.Equals(id, other.id) && 
      object.Equals(blah1, other.blah1) && 
      object.Equals(c1, other.c1); 
    } 

} 

public class child : IEquatable<child> 
{ 
    public String blah2 { get; set; } 
    public String blah3 { get; set; } 

    public override int GetHashCode() 
    { 
     unchecked // Overflow is fine, just wrap 
     { 
      int hash = 17; 
      // Suitable nullity checks etc, of course :) 
      hash = hash * 23 + blah2.GetHashCode(); 
      hash = hash * 23 + blah3.GetHashCode(); 
      return hash; 
     } 
    } 

    public bool Equals(child other) 
    { 
     return object.Equals(blah2, other.blah2) && 
      object.Equals(blah3, other.blah3); 
    } 

} 
} 

Кто-нибудь может определить мою ошибку (и)?

+4

+1 для полного примера. – SLaks

+1

Какая ошибка вы получаете? Каково намеренное поведение? Выполнение кода просто не отображает ничего и выходит, когда нажата клавиша. – jdmichal

+0

@jdmichal, поставить точку останова на консоли.readkey и посмотрите на тестовую переменную, он должен сказать count = 1 not 2 – Fredou

ответ

5

Вам необходимо переопределить метод Equals(object):

public override bool Equals(object obj) { 
    return Equals(obj as parent); 
} 

В object.Equals метод (в отличие от EqualityComparer<T>.Default) не использует интерфейс IEquatable. Поэтому, когда вы пишете object.Equals(c1, other.c1), он не вызывает ваш метод Child.Equals(Child).

Вы не обязательно должны это делать для parent, но вы действительно должны.

+0

Ошибка 'ConsoleApplication1.parent. Equals (ConsoleApplication1.parent) ': не найдено подходящего метода для переопределения – Fredou

+0

@Fredou: Я забыл добавить возвращаемый тип. – SLaks

+0

, это должно было бы предположить, что c1! = Null и EqualityComparer все равно, не так ли? – Fredou

0

При добавлении вычисления хэш вы можете захотеть попробовать что-то вроде

hash ^= id.GetHashCode();

Не уверен, если это то, что вызывает проблему.

+0

Я считаю, что его хеширование в порядке - и в любом случае, если это было плохо это приведет к плохой производительности, а не к некорректному выходу (пока хэш не будет детерминированным, что, очевидно, в этом случае). И проблема перманентности, конечно, не будет заметна только с двумя предметами. :) –

3

Либо вы делаете то, что предлагает SLaks, или вы используете EqualityComparer<child>.Default в своем классе parent использовать IEquatable<child> реализации:

public bool Equals(parent other) 
    { 
    return object.Equals(id, other.id) && 
    object.Equals(blah1, other.blah1) && 
    EqualityComparer<child>.Default.Equals(c1, other.c1); 
} 
+0

Эта работа, теперь мне нужно знать, что такое лучшая практика, реализующая это или способ SLaks – Fredou

+1

Вы ** действительно ** должны переопределять 'Equals (object)'. В противном случае вы, скорее всего, получите другие неприятные сюрпризы. – SLaks

+0

должен переопределить Equals (объект): Согласен. – ulrichb

0

Есть несколько вещей, чтобы получить здесь. Если я собираюсь реализовать любой аспект равенства в классе, таком как GetHashCode, переопределяя == или IEquatable, я всегда использую следующий шаблон.

  1. Override Равно
  2. Override GetHashCode
  3. Реализовать IEquatable<T>, что означает реализацию Equals(T)
  4. Реализовать! =
  5. Реализовать ==

Так что, если у меня был класс с именем ExpiryMonth с свойства Год и Месяц, так будет выглядеть эта реализация. Сейчас довольно бессмысленная задача адаптироваться к другим типам классов.

Я основывал этот шаблон на нескольких других ответах stackoverflow, которые все заслуживают доверия, но которые я не отслеживал по пути.

Всегда реализуя все эти элементы вместе, он обеспечивает правильные операции равенства в различных контекстах, включая словари и операции Linq.

public static bool operator !=(ExpiryMonth em1, ExpiryMonth em2) 
    { 
     if (((object)em1) == null || ((object)em2) == null) 
     { 
      return !Object.Equals(em1, em2); 
     } 
     else 
     { 
      return !(em1.Equals(em2)); 
     } 
    } 
    public static bool operator ==(ExpiryMonth em1, ExpiryMonth em2) 
    { 
     if (((object)em1) == null || ((object)em2) == null) 
     { 
      return Object.Equals(em1, em2); 
     } 
     else 
     { 
      return em1.Equals(em2); 
     } 
    } 
    public bool Equals(ExpiryMonth other) 
    { 
     if (other == null) { return false; } 
     return Year == other.Year && Month == other.Month; 
    } 
    public override bool Equals(object obj) 
    { 
     if (obj == null) { return false; } 
     ExpiryMonth em = obj as ExpiryMonth; 
     if (em == null) { return false; } 
     else { return Equals(em); } 
    } 
    public override int GetHashCode() 
    { 
     unchecked // Overflow is not a problem 
     { 
      var result = 17; 
      result = (result * 397) + Year.GetHashCode(); 
      result = (result * 397) + Month.GetHashCode(); 
      return result; 
     } 
    }