Вопрос

Я заметил эти два интерфейса, и несколько связанных классов были добавлены в .NET 4. Они кажутся немного лишними для меня; Я прочитал несколько блогов о них, но я до сих пор не могу понять, какую проблему они решают, это было сложно перед .NET 4.

Что используют IStructuralEquatable а также IStructuralComparable?

Это было полезно?

Решение

Все типы в .NET поддерживают Object.Equals() Метод, который по умолчанию сравнивает два типа для Опорное равенство. Отказ Однако иногда он также желательно иметь возможность сравнивать два типа для Структурное равенство.

Лучший пример этого являются массивы, которые с .NET 4 теперь реализуют IStructuralEquatable интерфейс. Это позволяет различать, сравниваете ли вы два массива для справочного равенства или для «структурного равенства» - о том, имеют ли они одинаковое количество элементов с одинаковыми значениями в каждой позиции. Вот пример:

int[] array1 = new int[] { 1, 5, 9 };
int[] array2 = new int[] { 1, 5, 9 };

// using reference comparison...
Console.WriteLine( array1.Equals( array2 ) ); // outputs false

// now using the System.Array implementation of IStructuralEquatable
Console.WriteLine( StructuralComparisons.StructuralEqualityComparer.Equals( array1, array2 ) ); // outputs true

Другие виды, которые реализуют структурное равенство / сопоставимость, включают кортежи и анонимные типы, которые оба явно извлекают выгоду от способности выполнять сравнение на основе их структуры и содержания.

Вопрос, который вы не спрашивали, это:

Почему у нас есть IStructuralComparable а также IStructuralEquatable когда уже существует IComparable а также IEquatable интерфейсы?

Ответ, который я бы предложил, это то, что, как правило, желательно дифференцироваться между ссылочными сравнениями и структурными сравнениями. Обычно ожидается, что если вы реализуете IEquatable<T>.Equals Вы также переопределите Object.Equals быть последовательным. В этом случае как бы вы поддерживаете как справочную, так и структурное равенство?

Другие советы

У меня такой же вопрос. Когда я бежал пример Любуна, я был удивлен, увидев, что у меня другой ответ! Несмотря на то, что этот ответ имеет 8 upvotes, это неправильно. После многих «отражателя», вот мой взять на себя.

Некоторые контейнеры (массивы, кортежи, анонимные типы) поддерживают IstructuralComparable и IstructuraleQuatable.

IstructuralComparable поддерживает глубокую сортировку по умолчанию.
Istructuralequatable поддерживает глубокую, по умолчанию хеширование.

{Обратите внимание, что EqualityComparer<T> Поддерживает мелкую (только 1 уровень контейнера), по умолчанию хеширование.}

Насколько я вижу, это выставлено только через класс StructuralComParisons. Единственный способ, которым я могу понять, чтобы сделать это полезным, это сделать StructuralEqualityComparer<T> Класс помощника как следует:

    public class StructuralEqualityComparer<T> : IEqualityComparer<T>
    {
        public bool Equals(T x, T y)
        {
            return StructuralComparisons.StructuralEqualityComparer.Equals(x,y);
        }

        public int GetHashCode(T obj)
        {
            return StructuralComparisons.StructuralEqualityComparer.GetHashCode(obj);
        }

        private static StructuralEqualityComparer<T> defaultComparer;
        public static StructuralEqualityComparer<T> Default
        {
            get
            {
                StructuralEqualityComparer<T> comparer = defaultComparer;
                if (comparer == null)
                {
                    comparer = new StructuralEqualityComparer<T>();
                    defaultComparer = comparer;
                }
                return comparer;
            }
        }
    }

Теперь мы можем сделать хеш-метатель с элементами, имеющими контейнеры в контейнерах в контейнерах.

        var item1 = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
        var item1Clone = Tuple.Create(1, new int[][] { new int[] { 1, 2 }, new int[] { 3 } });
        var item2 = Tuple.Create(1, new int[][] { new int[] { 1, 3 }, new int[] { 3 } });

        var set = new HashSet<Tuple<int, int[][]>>(StructuralEqualityComparer<Tuple<int, int[][]>>.Default);
        Console.WriteLine(set.Add(item1));      //true
        Console.WriteLine(set.Add(item1Clone)); //false
        Console.WriteLine(set.Add(item2));      //true

Мы также можем заставить наш контейнер хорошо играть с этими другими контейнерами, реализуя эти интерфейсы.

public class StructuralLinkedList<T> : LinkedList<T>, IStructuralEquatable
    {
        public bool Equals(object other, IEqualityComparer comparer)
        {
            if (other == null)
                return false;

            StructuralLinkedList<T> otherList = other as StructuralLinkedList<T>;
            if (otherList == null)
                return false;

            using( var thisItem = this.GetEnumerator() )
            using (var otherItem = otherList.GetEnumerator())
            {
                while (true)
                {
                    bool thisDone = !thisItem.MoveNext();
                    bool otherDone = !otherItem.MoveNext();

                    if (thisDone && otherDone)
                        break;

                    if (thisDone || otherDone)
                        return false;

                    if (!comparer.Equals(thisItem.Current, otherItem.Current))
                        return false;
                }
            }

            return true;
        }

        public int GetHashCode(IEqualityComparer comparer)
        {
            var result = 0;
            foreach (var item in this)
                result = result * 31 + comparer.GetHashCode(item);

            return result;
        }

        public void Add(T item)
        {
            this.AddLast(item);
        }
    }

Теперь мы можем сделать хеш-расстройство с элементами, имеющими контейнеры в рамках пользовательских контейнеров в контейнерах.

        var item1 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
        var item1Clone = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 2 }, new int[] { 3 } });
        var item2 = Tuple.Create(1, new StructuralLinkedList<int[]> { new int[] { 1, 3 }, new int[] { 3 } });

        var set = new HashSet<Tuple<int, StructuralLinkedList<int[]>>>(StructuralEqualityComparer<Tuple<int, StructuralLinkedList<int[]>>>.Default);
        Console.WriteLine(set.Add(item1));      //true
        Console.WriteLine(set.Add(item1Clone)); //false
        Console.WriteLine(set.Add(item2));      //true

Вот еще один пример, который иллюстрирует возможное использование двух интерфейсов:

var a1 = new[] { 1, 33, 376, 4};
var a2 = new[] { 1, 33, 376, 4 };
var a3 = new[] { 2, 366, 12, 12};

Debug.WriteLine(a1.Equals(a2)); // False
Debug.WriteLine(StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)); // True

Debug.WriteLine(StructuralComparisons.StructuralComparer.Compare(a1, a2)); // 0
Debug.WriteLine(StructuralComparisons.StructuralComparer.Compare(a1, a3)); // -1

Описание IStructuralEquatable Интерфейс говорит (в разделе «Примечания»):

То IStructuralEquatable Интерфейс позволяет реализовывать индивидуальные сравнения для проверки структурного равенства Коллекция объектов.

Это также проясняется тем, что этот интерфейс проживает в System.Collections пространство имен.

F # начал использовать их с .NET 4. ( .net 2 здесь)

Эти интерфейсы имеют решающее значение для F #

let list1 = [1;5;9] 
let list2 = List.append [1;5] [9]

printfn "are they equal? %b" (list1 = list2)

list1.GetType().GetInterfaces().Dump()

enter image description here

C # в двух словах книга:

Потому что массив - это класс, массивы всегда (сами) reference types, независимо от типа элемента массива. Это означает, что утверждение arrayB = arrayA Приводит к двум переменным, которые ссылаются на одинаковый массив. Аналогичным образом, два отдельных массива всегда не пройдут испытание на равенство - если вы не используете Custom Several Comparer. Framework 4.0 представил один для сравнения элементов в массивах, которые вы можете получить доступ через StructuralComparisons тип.

object[] a1 = { "string", 123, true};
object[] a2 = { "string", 123, true};

Console.WriteLine(a1 == a2);               // False
Console.WriteLine(a1.Equals(a2));          // False

IStructuralEquatable se1 = a1;
Console.WriteLine(se1.Equals(a2, StructuralComparisons.StructuralEqualityComparer));    // True
Console.WriteLine(StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2));     // True

object[] a3 = {"string", 123, true};
object[] a4 = {"string", 123, true};
object[] a5 = {"string", 124, true};

IStructuralComparable se2 = a3;
Console.WriteLine(se2.CompareTo(a4, StructuralComparisons.StructuralComparer));    // 0
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a3, a4));       // 0
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a4, a5));       // -1
Console.WriteLine(StructuralComparisons.StructuralComparer.Compare(a5, a4));       // 1
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top