Пересекайтесь с пользовательским IEqualityComparer, используя Linq

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

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

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

Вот характеристика моих коллекций Коллекции Good и Default:

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

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

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

Вот, что я думаю, моя лучшая попытка:

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

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

Ответ 1

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

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

Если хэш-код по-разному отличается, то соответствующие 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);
}

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

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 -

Ответ 2

Вам нужно проверить фактическое равенство, а не только равенство hashcode.

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

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

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

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

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

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