Правильный способ написать GetHashCode(), когда Equality Comparer основан на операции ИЛИ?

Я пытаюсь написать компаратор равенства для простого класса с 3 полями, например:

public class NumberClass
{
    public int A { get; set; }
    public int B { get; set; }
    public int C { get; set; }
}

Мое условие равенства двух объектов NumberClass состоит в том, что Obj1.A == Obj2.A || Obj1.B == Obj2.B (другими словами, ИЛИ), Obj1 и Obj2 являются экземплярами NumberClass.

Я могу легко записать Equals() моего компаратора следующим образом, но я не знаю, что делать с моим методом GetHashCode().

public bool Equals(NumberClass x, NumberClass y)
{
    if (x.A == y.A || x.B == y.B)
        return true;
    else
        return false;
}

public int GetHashCode(NumberClass obj)
{
    return ???
}

Если бы моим условием равенства было И вместо ИЛИ, я мог бы написать свой GetHashCode() следующим образом, взятый из этого ответа SO.

public int GetHashCode(NumberClass obj)
{
    unchecked
    {
        int hash = 17;
        if (obj != null)
        {
            hash = hash * 23 + obj.A.GetHashCode();
            hash = hash * 23 + obj.B.GetHashCode();
        }
        return hash;
    }
}

Но это, очевидно, не будет работать для ИЛИ, поскольку только одно из A или B достаточно, чтобы мое условие равенства было истинным.

Один обходной путь, о котором я мог подумать, всегда возвращает одно и то же значение в GetHashCode(), которого было бы достаточно для операций LINQ, таких как Distinct(), но я чувствую, что должен быть другой способ, поскольку у него есть свои недостатки.

Как правильно поступить в этой ситуации?

P.S. Для тестирования представьте, что мой Main() выглядит следующим образом:

static void Main(string[] args)
{
    List<NumberClass> list = new List<NumberClass>();
    list.Add(new NumberClass { A = 1, B = 2, C = 3 });
    list.Add(new NumberClass { A = 1, B = 22, C = 33 });

    var distinct = list.Distinct(new NumberComparer());
    Console.ReadKey();
}

Я ожидаю, что distinct будет содержать только первый элемент в списке.


person Sach    schedule 09.08.2017    source источник


Ответы (1)


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

Вы просто не сможете использовать какие-либо алгоритмы на основе хэшей, пока у вас есть такое «нечеткое» равенство.

person Servy    schedule 09.08.2017
comment
Не могли бы вы объяснить, что вы имели в виду под предполагается, что равенство будет транзитивным? - person Sach; 10.08.2017
comment
@Sach Если A равно B, а B равно C, то A равно C (если равенство транзитивно). В вашем случае это не так. - person Servy; 10.08.2017
comment
Понял, спасибо! Итак, я думаю, что единственный обходной путь в такой ситуации - вернуть то же значение в GetHashCode() или просто не использовать компаратор и выполнить сравнение старой школы. - person Sach; 10.08.2017
comment
@Sach Как я уже сказал, вы не можете использовать алгоритм, который использует хэши для сравнения на равенство. Кроме того, типичное определение отличительного даже концептуально не имеет смысла, учитывая ваше определение равенства, поэтому вы не только не можете использовать определение .NET, но я даже не знаю, как вы его реализуете (например, вы нужно определить, что вы хотите сделать, потому что типичное поведение не имеет смысла, учитывая ваше определение равенства). Вам нужно будет написать что-то с нуля. - person Servy; 10.08.2017
comment
Я тоже никогда раньше не использовал такое равенство. Причина, по которой я задал этот вопрос, заключается в том, что один пользователь задал его в комментарии к одному из моих ответов SO, и я был заинтригован. - person Sach; 10.08.2017
comment
@Sach Этот тип равенства чаще всего возникает при работе с числами с плавающей запятой; поскольку они имеют неотъемлемое приближение к своему значению, при сравнении их вы часто говорите, что они равны, если они находятся в пределах определенного порога друг друга. Это единственное практическое применение, которое приходит на ум. - person Servy; 10.08.2017
comment
Это хорошо знать. Это была поучительная дискуссия, спасибо! - person Sach; 10.08.2017