Размер шрифта:
Как определить равенство значений для класса или структуры. Руководство по программированию на C# | Microsoft Docs

Как определить равенство значений для класса или структуры. Руководство по программированию на C# | Microsoft Docs

Как определить равенство значений для класса или структуры (руководство по программированию на C#)

Записи автоматически реализуют равенство значений. Попробуйте определить record вместо class , когда ваш тип моделирует данные и должен реализовать равенство значений.

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

В любом случае, реализация как для классов, так и для структур, должна соответствовать следующим пяти принципам обеспечения эквивалентности (в следующих правилах предполагается, что x , y и z не равны NULL):

Рефлексивное свойство: x.Equals(x) возвращает true .

Симметричное свойство: x.Equals(y) возвращает то же значение, что и y.Equals(x) .

Транзитивное свойство: если (x.Equals(y) && y.Equals(z)) возвращает true , x.Equals(z) возвращает true .

Последовательные вызовы x.Equals(y) возвращают одно и то же значение до тех пор, пока не будут изменены объекты, на которые ссылаются x и y.

Любое значение, отличающееся от NULL, не равно NULL. Поэтому x.Equals(y) вызывает исключение, если x имеет значение NULL. Это нарушает правила 1 или 2 в зависимости от аргумента для Equals .

Любая определяемая вами структура имеет заданную по умолчанию реализацию равенства значений, которая наследуется от переопределения System.ValueType метода Object.Equals(Object). Эта реализация использует отражение для проверки всех полей и свойств в типе. Хотя эта реализация возвращает верный результат, она отличается невысокой скоростью по сравнению с пользовательской реализацией, которую можно написать специально для конкретного типа.

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

Переопределите виртуальный метод. В большинстве случаев пользовательская реализация bool Equals( object obj ) должна вызывать относящийся к конкретному типу метод Equals , который является реализацией интерфейса System.IEquatable<T>. (См. шаг 2.)

Реализуйте интерфейс System.IEquatable<T>, предоставив метод Equals для конкретного типа. Именно на этом этапе происходит фактическое сравнение значений. Например, функцию равенства можно определить путем сравнения только одного из двух полей в типе. Не создавайте исключения в Equals . Для классов, которые связаны наследованием, должно соблюдаться следующее.

этот метод должен проверять только те поля, которые объявлены в классе. Он должен вызывать метод base.Equals для проверки полей в базовом классе. (Не вызывайте base.Equals , если тип наследует напрямую от Object, так как реализация Object для Object.Equals(Object) выполняет проверку равенства ссылок.)

Две переменные должны считаться равными, только если совпадают их типы времени выполнения. Также убедитесь, что реализация IEquatable метода Equals для типов времени выполнения используется, если типы времени выполнения и времени компиляции переменной отличаются. Одна из стратегий обеспечения правильного сравнения типов времени выполнения — реализация IEquatable только в классах sealed . Дополнительные сведения см. в примере класса ниже в этой статье.

Необязательно, но рекомендуется: перегрузите операторы == и ==.

Переопределите Object.GetHashCode таким образом, чтобы два объекта с равными значениями создавали одинаковый хэш-код.

Необязательно. для поддержки определений для "больше чем" или "меньше чем" реализуйте IComparable<T> интерфейс для вашего типа, а также перегрузите IComparable<T> и < .

Начиная с версии C# 9.0 можно использовать записи для получения семантики равенства значений без ненужного стандартного кода.

Пример класса

В следующем примере показана реализация равенства значений в классе (ссылочный тип).

В классах (ссылочных типах) реализация по умолчанию обоих методов Object.Equals(Object) выполняет проверку равенства ссылок, а не значений. Когда разработчик переопределяет виртуальный метод, его задача заключается в том, чтобы реализовать семантику равенства значений.

К объектам класса можно применять операторы == и != , даже если они не были перегружены в классе. Однако по умолчанию они служат для проверки равенства ссылок. При перегрузке в классе метода Equals необходимо перегрузить операторы == и != , но это необязательно.

Приведенный выше код может не поддерживать все сценарии наследования, как ожидается. Рассмотрим следующий код.

Этот код сообщает, что p1 и p2 равны несмотря на разницу в значениях z . Эта разница игнорируется, так как компилятор выбирает реализацию TwoDPoint для IEquatable на основе типа времени компиляции.

Встроенный механизм обеспечения равенства значений типов record поддерживает такие сценарии. Если бы TwoDPoint и ThreeDPoint имели тип record , результатом p1.Equals(p2) было бы значение False . См. дополнительные сведения о равенстве в иерархиях наследования типа .

Пример структуры

В следующем примере показана реализация равенства значений в структуре (тип значения).

Для структур реализация по умолчанию Object.Equals(Object) (представляет собой переопределенную версию в System.ValueType) выполняет проверку равенства значений посредством отражения, сравнивая значения каждого поля в типе. Когда разработчик переопределяет виртуальный метод Equals в структуре, его задача состоит в том, чтобы найти более эффективный способ проверки равенства значений и, если это возможно, реализовать сравнение только на основании части полей или свойств структуры.

Операторы == и == нельзя применять к структурам, если только они явным образом не перегружены для структуры.

📎📎📎📎📎📎📎📎📎📎