2010-12-02 2 views
13

Короче говоря: у меня есть 2 коллекции объектов. Один содержит хорошие значения (назовем его «Хорошо»), другие значения по умолчанию (г-н «По умолчанию»). Я хочу, чтобы Пересечение Союза между Хорошим и По умолчанию и По умолчанию. Другими словами: Intersect (Union (Good, Default), Default). Можно подумать, что он разрешен как Default, но здесь он становится сложным: я использую пользовательский IEqualityComparer.Пересечение с помощью пользовательского IEqualityComparer с использованием Linq

я получил следующие классы:

class MyClass 
{ 
    public string MyString1; 
    public string MyString2; 
    public string MyString3; 
} 

class MyEqualityComparer : IEqualityComparer<MyClass> 
{ 
    public bool Equals(MyClass item1, MyClass item2) 
    { 
     if(item1 == null && item2 == null) 
      return true; 
     else if((item1 != null && item2 == null) || 
       (item1 == null && item2 != null)) 
      return false; 

     return item1.MyString1.Equals(item2.MyString1) && 
       item1.MyString2.Equals(item2.MyString2); 
    } 

    public int GetHashCode(MyClass item) 
    { 
     return new { item.MyString1, item.MyString2 }.GetHashCode(); 
    } 
} 

Вот характерные моей коллекции Хороших и коллекции по умолчанию:

По умолчанию: Это большой набор, содержащий все хотели {MyString1, MyString2} пары , но значения MyString3, как вы можете догадаться, являются значениями по умолчанию.

Хорошо: это меньший набор, содержащий в основном элементы, которые находятся в наборе по умолчанию, но с некоторыми хорошими значениями MyString3. Он также имеет некоторые {MyString1, MyString2}, которые находятся за пределами желаемого набора.

Что я хочу сделать, так это: Возьмите только предметы из Хороших, которые находятся в разделе «По умолчанию», но добавьте в него другие элементы по умолчанию.

Вот, что я думаю, мой лучший попробовать:

HalfWantedResult = Good.Union(Default, new MyEqualityComparer()); 
WantedResult= HalfWantedResult.Intersect(Good, new MyEqualityComparer()); 

Я учил он должен работал, но результат я получаю в основном только хорошее {MyString1, MyString2} набор пар, но все начиная с установленного по умолчанию, поэтому у меня есть значение по умолчанию. Я также попытался переключить Default и Good последнего Intersect, но получаю тот же результат.

+3

Ваша реализация Равно действительно плохо.Будут столкновения хэшей, которых не должно быть. Почему бы не использовать одну и ту же проекцию (`new {item.MyString1, item.MyString2}`), но вызвать Equals? – 2010-12-02 21:48:11

+0

Я должен изучить это, это может быть частью проблемы. Союз использует GetHashCode, а Intersects использует Equals. Я действительно не преподавал эту часть. * стыдно * – Tipx 2010-12-02 21:51:20

ответ

18

Прежде всего, это не так:

public bool Equals(MyClass item1, MyClass item2) 
{ 
    return GetHashCode(item1) == GetHashCode(item2); 
} 

Если Hashcode различны точно соответствующие 2 детали различны, но, если они равны, не гарантируется, что соответствующие элементы 2 равны.

Так что это правильный Equals реализация:

public bool Equals(MyClass item1, MyClass item2) 
{ 
    if(object.ReferenceEquals(item1, item2)) 
     return true; 
    if(item1 == null || item2 == null) 
     return false; 
    return item1.MyString1.Equals(item2.MyString1) && 
      item1.MyString2.Equals(item2.MyString2); 
} 

Как Slacks suggested (предвидя меня) код выглядит следующим образом:

var Default = new List<MyClass> 
{ 
    new MyClass{MyString1="A",MyString2="A",MyString3="-"}, 
    new MyClass{MyString1="B",MyString2="B",MyString3="-"}, 
    new MyClass{MyString1="X",MyString2="X",MyString3="-"}, 
    new MyClass{MyString1="Y",MyString2="Y",MyString3="-"}, 
    new MyClass{MyString1="Z",MyString2="Z",MyString3="-"}, 

}; 
var Good = new List<MyClass> 
{ 
    new MyClass{MyString1="A",MyString2="A",MyString3="+"}, 
    new MyClass{MyString1="B",MyString2="B",MyString3="+"}, 
    new MyClass{MyString1="C",MyString2="C",MyString3="+"}, 
    new MyClass{MyString1="D",MyString2="D",MyString3="+"}, 
    new MyClass{MyString1="E",MyString2="E",MyString3="+"}, 
}; 
var wantedResult = Good.Intersect(Default, new MyEqualityComparer()) 
         .Union(Default, new MyEqualityComparer()); 

// wantedResult: 
// A A + 
// B B + 
// X X - 
// Y Y - 
// Z Z - 
+0

+1 за то, что помог мне с моими равными, но не принят, так как он не решает проблему. (Я бы хотел, чтобы я мог +2!: -P) – Tipx 2010-12-02 22:01:48

10

Вы должны проверить фактическое равенство, а не только Hashcode равенство ,

GetHashCode() не является (и не может быть) без конфликтов, поэтому метод Equals необходим в первую очередь.

Кроме того, вы можете сделать это гораздо проще, написав

WantedResult = Good.Concat(Default).Distinct(); 

Distinct метод возвращает первый элемент каждой пары дублей, так что это будет возвращать желаемого результата.

EDIT: Это должно быть

WantedResult = Good.Intersect(Default, new MyEqualityComparer()) 
        .Union(Default, new MyEqualityComparer()); 
Смежные вопросы