Язык программирования C#9 и платформа .NET5
При переопределении метода
для класса, расширяющего специальный базовый класс, первым делом необходимо получить возвращаемое значениеToString()из родительского класса, используя ключевое словоToString(). После получения строковых данных родительского класса их можно дополнить специальной информацией производного класса.baseПереопределение метода System.Object.Equals()
Давайте также переопределим поведение метода
, чтобы работать с семантикой на основе значений. Вспомните, что по умолчаниюObject.Equals()возвращаетEquals(), только если два сравниваемых объекта ссылаются на один и тот же экземпляр объекта в памяти. Для классаtrueможет оказаться полезной такая реализацияPerson, которая возвращаетEquals(), если две сравниваемые переменные содержат те же самые значения состояния (например, фамилию, имя и возраст).trueПрежде всего, обратите внимание, что входной аргумент метода
имеет общий типEquals(). В связи с этим первым делом необходимо удостовериться в том, что вызывающий код действительно передал экземпляр типаSystem.Object, и для дополнительной подстраховки проверить, что входной параметр не является ссылкойPerson.nullПосле того, как вы установите, что вызывающий код передал выделенный экземпляр
, один из подходов предусматривает реализацию методаPersonдля сравнения поле за полем данных входного объекта с данными текущего объекта:Equals()public override bool Equals(object obj){if (!(obj is Person temp)){return false;}if (temp.FirstName == this.FirstName&& temp.LastName == this.LastName&& temp.Age == this.Age){return true;}return false;}Здесь производится сравнение значений входного объекта с внутренними значениями текущего объекта (обратите внимание на применение ключевого слова
). Если имя, фамилия и возраст в двух объектах идентичны, то эти два объекта имеют одинаковые данные состояния и возвращается значениеthis. Любые другие результаты приводят к возвращениюtrue.falseХотя такой подход действительно работает, вы определенно в состоянии представить, насколько трудоемкой была бы реализация специального метода
для нетривиальных типов, которые могут содержать десятки полей данных. Распространенное сокращение предусматривает использование собственной реализации методаEquals(). Если класс располагает подходящей реализациейToString(), в которой учитываются все поля данных вверх по цепочке наследования, тогда можно просто сравнивать строковые данные объектов (проверив на равенствоToString()):null// Больше нет необходимости приводить obj к типу Person,// т.к. у всех типов имеется метод ToString().public override bool Equals(object obj)=> obj?.ToString() == ToString();Обратите внимание, что в этом случае нет необходимости проверять входной аргумент на принадлежность к корректному типу (
в нашем примере), поскольку методPersonподдерживают все типы .NET. Еще лучше то, что больше не требуется выполнять проверку на предмет равенства свойство за свойством, т.к. теперь просто проверяются значения, возвращаемые методомToString().ToString()Переопределение метода System.Object.GetHashCode()
В случае переопределения в классе метода
вы также должны переопределить стандартную реализацию методаEquals(). Выражаясь упрощенно, хеш-код — это числовое значение, которое представляет объект как специфическое состояние. Например, если вы создадите две переменные типаGetHashCode(), хранящие значениеstring, то они должны давать один и тот же хеш-код. Однако если одна из них хранит строку в нижнем регистре (Hello), то должны получаться разные хеш-коды.helloДля выдачи хеш-значения метод
по умолчанию применяет адрес текущей ячейки памяти, где расположен объект. Тем не менее, если вы строите специальный тип, подлежащий хранению в экземпляре типаSystem.Object.GetHashCode()(из пространства именHashtable), тогда всегда должны переопределять данный член, потому что для извлечения объекта типSystem.Collectionsбудет вызывать методыHashtableиEquals().GetHashCode()На заметку! Говоря точнее, класс
внутренне вызывает методSystem.Collections.Hashtable, чтобы получить общее представление о местоположении объекта, а с помощью последующего (внутреннего) вызова методаGetHashCode()определяет его точно.Equals()Хотя в настоящем примере мы не собираемся помещать объекты
внутрьPerson, ради полноты изложения давайте переопределим методSystem.Collections.Hashtable. Существует много алгоритмов, которые можно применять для создания хеш-кода, как весьма изощренных, так и не очень. В большинстве ситуаций есть возможность генерировать значение хеш-кода, полагаясь на реализацию методаGetHashCode()из классаGetHashCode().System.StringУчитывая, что класс
уже имеет эффективный алгоритм хеширования, использующий для вычисления хеш-значения символьные данные объектаString, вы можете просто вызвать методStringс той частью полей данных, которая должна быть уникальной во всех экземплярах (вроде номера карточки социального страхования), если ее удается идентифицировать. Таким образом, если в классеGetHashCode()определено свойствоPerson, то вы могли бы написать следующий код:SSN