2013-07-03 1 views
0

Возможно ли выполнить объединение/исключение в списках объектов, где экземпляр объектов не обязательно один и тот же, но они функционально эквивалентны?Union/За исключением списков с разными экземплярами объектов

Что я имею в виду, если у меня есть класс, как это,

Class A 
{ 
    String a; 
    int b; 
    double c; 
} 

И я следующие списки:

A foo = new A() {"a",2,3.4}  
A bar = new A() {"a",2,3.4} 

List<A> firstList = new List<A>() { foo } 
List<A> secondList = new List<A>() { bar } 

Как я могу выполнить firstList.Except/Союз на secondList если firstList и у secondList были совершенно разные экземпляры объектов, но поля/свойства объектов были точно такими же?

+1

Как насчет переопределения метода 'object.Equals()'? Тогда все методы Linq, которые проверяют равенство, будут использовать ваш метод, который должен проверять равенство на основе полей. – feralin

+1

И если вы переопределите метод 'Equals', вы также должны переопределить метод GetHashCode. – Servy

+0

На самом деле, я добавлю это как ответ. – feralin

ответ

1

Вам необходимо перегрузить метод Equals вашего класса.

Прямо сейчас, как он проверяет равенство, проверяя ссылку. Там есть способ исправить это путем переопределения метода Equals:

class A 
{ 
    string a; 
    int b; 
    double c; 
    public override bool Equals(object obj) 
    { 
     A aobj = obj as A; 
     if (aobj == null) return false; 
     return a == aobj.a && b == aobj.b && c == aobj.c; 
    } 
} 

Однако, поскольку эти функции выполнять в лучшем случае, вам также необходимо переопределить метод GetHashCode, тоже. Как это:

class A 
{ 
    string a; 
    int b; 
    double c; 
    public override bool Equals(object obj) 
    { 
     return a == obj.a && b == obj.b && c == obj.c; 
    } 
    public override int GetHashCode() 
    { 
     unchecked { return 17 * (a ?? "").GetHashCode() * b * c.GetHashCode(); } 
    } 
} 
+0

Вы не хешируете 'b' в своей функции хэш-кода get. Кроме того, умножение на 17 на самом деле ничего не делает, вы можете просто удалить его. – Servy

+0

Ваш код вызовет ошибку компилятора; class 'object' не имеет полей' a', 'b' или' c'. – feralin

+0

Nevermind, вы его отредактировали :) – feralin

1

Просто переопределить метод object.Equals сказать миру, когда лечить ваши объекты как равные. Ваш class A должен выглядеть примерно так:

class A 
{ 
    string a; 
    int b; 
    double c; 

    public override bool Equals(object obj) 
    { 
     if (!(obj is A)) return obj.Equals(this); // defer to other object 
     A other = (A)obj; 
     return a == other.a && b == other.b && c == other.c; // check field equality 
    } 
    public override int GetHashCode() 
    { 
     int hc = 13; 
     hc += a.GetHashCode() * 27; 
     hc += b.GetHashCode() * 27; 
     hc += c.GetHashCode() * 27; 
    } 
} 
+0

Просто FYI:' int.GetHashCode' бесполезен. Это 'return this;' –

+0

@newStackExchangeInstance meh, на самом деле не имеет значения. Это также зависит от реализации, поэтому также можно использовать метод GetHashCode. – feralin

+0

Ну, давайте посмотрим: что ** еще ** вернется? GetHashCode возвращает 'int' ... –

0

Добавление немного больше предыдущих ответов. Переопределение Equals потребуется переопределение == и =

public class A 
{ 
    String a; 
    int b; 
    double c; 

    public override bool Equals(object obj) 
    { 
     if (object.ReferenceEquals(null, obj)) 
     { 
      return false; 
     } 

     if (object.ReferenceEquals(this, obj)) 
     { 
      return true; 
     } 

     if (obj.GetType() != typeof(A)) 
     { 
      return false; 
     } 

     var other = obj as A; 
     return string.Equals(this.a, other.a) && this.b == other.b && this.c == other.b; 
    } 

    public override int GetHashCode() 
    { 
     if (string.IsNullOrEmpty(a)) 
     { 
      return this.b.GetHashCode()^this.c.GetHashCode(); 
     } 
     return this.a.GetHashCode()^this.b.GetHashCode()^this.c.GetHashCode(); 
    } 

    public static bool operator ==(A left, A right) 
    { 
     if (object.ReferenceEquals(left, right)) 
     { 
      return true; 
     } 

     if (object.ReferenceEquals(null, left)) 
     { 
      return false; 
     } 

     if (object.ReferenceEquals(null, right)) 
     { 
      return false; 
     } 

     return left.Equals(right); 
    } 

    public static bool operator !=(A left, A right) 
    { 
     return !(left == right); 
    } 
} 
+0

Нет, нет. Однако это ожидаемое поведение. –

+0

Я имел в виду, что в целом реализация ==! = Хорошая практика; в противном случае вы не получите непоследовательный результат, основанный на проверке равенства? – loopedcode

0

Вы можете использовать LINQ к объектам для создания пересечений и объединений в списках - и нет никакой необходимости переопределять Equals и GetHashCode. Используйте Enumerable.Intersect и Enumerable.Except методы:

public class A 
{ 
    public String a; 
    public int b; 
    public double c; 
} 
A foo = new A() {a = "a", b = 2, c = 3.4}; 
A bar = new A() {a = "a", b = 2, c = 3.4}; 

List<A> firstList = new List<A>() { foo }; 
List<A> secondList = new List<A>() { bar }; 

firstList.Except(secondList); 
firstList.Intersect(secondList); 

Выходной сигнал в этом случае:

same entries: 
>> IEnumerable<A> (1 item) 4 
>> a b c 
>> a 2 3,4 

Вы можете сделать комбинации firstList.Method (secondsList) и наоборот. Чтобы сравнить сложные типы объектов, вы можете написать custom Comparer - IEqualityComparer.

+0

Итак, вы обходитесь с помощью анонимного объекта? Отходы ресурсов. –

 Смежные вопросы

  • Нет связанных вопросов^_^