Как реализовать IEquatable с различными проверками равенства - PullRequest
0 голосов
/ 12 марта 2020

У меня есть MyCustomSet класс с IEquatable , реализованный, как показано ниже.

Это работает fantasti c когда я хочу проверить равенство для всех трех наборов (SetA *, SetB * и Set C*). Но требования диктуют, что мне также необходима возможность проверки равенства только для SetA *, SetB * или Set C* или их комбинации (SetA * и SetB *, SetA * и Set C* или SetB * и Установите C*) и игнорируйте проверку равенства для любого другого набора, который не требуется в проверке.

В настоящее время я использую foreach и LINQ для итерируйте по множествам, чтобы выполнить частичные проверки на равенство, и это работает, но это не очень эффективно для больших наборов данных.

Может быть, ответ смотрит на меня прямо в лицо, но я не вижу это потому, что я понятия не имею, как реализовать IEquatable , который может обрабатывать различные проверки на равенство.

Кто-нибудь поможет мне с некоторыми предложениями или указаниями относительно того, как это может быть реализовано? Пример будет еще более ценным.

public static class HashCode
{
    public const int Start = 17;

    public static int Hash<T>(this int hash, T obj)
    {
        var h = EqualityComparer<T>.Default.GetHashCode(obj);
        return unchecked((hash * 439) + h);
    }
}

public class MyCustomSetModel
{
    public sealed class MyCustomSet : IEquatable<MyCustomSet>
    {
        public string ItemName { get; set; }
        public string ItemType { get; set; }

        public double SetA1 { get; set; }
        public double SetA2 { get; set; }
        public double SetA3 { get; set; }

        public double SetB1 { get; set; }
        public double SetB2 { get; set; }
        public double SetB3 { get; set; }

        public double SetC1 { get; set; }
        public double SetC2 { get; set; }
        public double SetC3 { get; set; }

        public bool Equals(MyCustomSet other)
        {
            if (ReferenceEquals(other, null))
            {
                return false;
            }

            if (ReferenceEquals(other, this))
            {
                return true;
            }

            return
                (
                (this.ItemName == other.ItemName) &&
                (this.ItemType == other.ItemType) &&

                (this.SetA1 == other.SetA1) &&
                (this.SetA2 == other.SetA2) &&
                (this.SetA3 == other.SetA3) &&

                (this.SetB1 == other.SetB1) &&
                (this.SetB2 == other.SetB2) &&
                (this.SetB3 == other.SetB3) &&

                (this.SetC1 == other.SetC1) &&
                (this.SetC2 == other.SetC2) &&
                (this.SetC3 == other.SetC3)
                );
        }

        public override bool Equals(object obj) => Equals(obj as MyCustomSet);

        public override int GetHashCode()
        {
            unchecked
            {
                return HashCode.Start
                    .Hash(ItemName)
                    .Hash(ItemType)

                    .Hash(SetA1)
                    .Hash(SetA2)
                    .Hash(SetA3)

                    .Hash(SetB1)
                    .Hash(SetB2)
                    .Hash(SetB3)

                    .Hash(SetC1)
                    .Hash(SetC2)
                    .Hash(SetC3);
            }
        }
    }
}

* Обновление: *

Спасибо Мэтью Уотсону, который указал мне правильное направление. Поэтому я реализовал пользовательский компаратор следующим образом. Похоже, что это работает, но если кто-то видит потенциальные проблемы или возможности для лучшей реализации, пожалуйста, не стесняйтесь комментировать.

        public sealed class MyCustomSetComparer : IEqualityComparer<MyCustomSet>
        {
            private bool _compareSetA;
            private bool _compareSetB;
            private bool _compareSetC;

            public MyCustomSetComparer(bool compareSetA = true, bool compareSetB = true, bool compareSetC = true)
            {
                _compareSetA = compareSetA;
                _compareSetB = compareSetB;
                _compareSetC = compareSetC;
            }

            public bool Equals(MyCustomSet x, MyCustomSet y)
            {
                if (Object.ReferenceEquals(x, y))
                {
                    return true;
                }

                if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
                {
                    return false;
                }

                bool result =
                    (x.ItemName == y.ItemName) &&
                    (x.ItemType == y.ItemType);

                if (_compareSetA)
                {
                    result = result &&
                        (x.SetA1 == y.SetA1) &&
                        (x.SetA2 == y.SetA2) &&
                        (x.SetA3 == y.SetA3);
                }

                if (_compareSetB)
                {
                    result = result &&
                        (x.SetB1 == y.SetB1) &&
                        (x.SetB2 == y.SetB2) &&
                        (x.SetB3 == y.SetB3);
                }

                if (_compareSetC)
                {
                    result = result &&
                        (x.SetC1 == y.SetC1) &&
                        (x.SetC2 == y.SetC2) &&
                        (x.SetC3 == y.SetC3);
                }

                return result;
            }

            public int GetHashCode(MyCustomSet item)
            {
                if (Object.ReferenceEquals(item, null))
                {
                    return 0;
                }

                int hash = HashCode.Start
                    .Hash(item.ItemName)
                    .Hash(item.ItemType);

                if (_compareSetA)
                {
                    hash = hash.Hash(item.SetA1)
                        .Hash(item.SetA2)
                        .Hash(item.SetA3);
                }

                if (_compareSetB)
                {
                    hash = hash.Hash(item.SetB1)
                        .Hash(item.SetB2)
                        .Hash(item.SetB3);
                }

                if (_compareSetC)
                {
                    hash = hash.Hash(item.SetC1)
                        .Hash(item.SetC2)
                        .Hash(item.SetC3);
                }

                unchecked
                {
                    return hash;
                }
            }
        }

1 Ответ

2 голосов
/ 13 марта 2020

Согласно комментарию Мэтью Уотсона, это идеальное время для использования IEqualityComparer<T> вместо IEquatable<T>. Я бы предложил реализовать IEquatable<T> только тогда, когда есть единственное очевидное, естественное определение равенства для типа. Если существует несколько вариантов равенства, и ни один из них не является более разумным, чем другие, реализуйте IEqualityComparer<T> - либо в нескольких различных типах реализации, либо в одном типе реализации, параметризованном в терминах создания.

...