// Указывают ли ссылки на один и тот же объект в памяти?
if (o.Equals(p1) && p2.Equals(o))
{
Console.WriteLine("Same instance!");
}
Console.ReadLine;
}
Вот вывод, получаемый в результате выполнения этого кода:
***** Fun with System.Object *****
ToString: ObjectOverrides.Person
Hash code: 58225482
Type: ObjectOverrides.Person
Same instance!
Обратите внимание на то, что стандартная реализация
ToString
возвращает полностью заданное имя текущего типа (
ObjectOverrides.Person
). Как будет показано в главе 15, где исследуется построение специальных пространств имен, каждый проект C# определяет "корневое пространство имен", название которого совпадает с именем проекта. Здесь мы создали проект по имени
ObjectOverrides
, поэтому тип
Person
и класс
Program
помещены внутрь пространства имен
ObjectOverrides
.
Стандартное поведение метода
Equals
заключается в проверке, указывают ли две переменные на один и тот же объект в памяти. В коде мы создаем новую переменную
Person
по имени
pi
. В этот момент новый объект
Person
помещается в управляемую кучу. Переменная
р2
также относится к типу
Person
. Тем не менее, вместо создания нового экземпляра переменной
р2
присваивается ссылка
pi
. Таким образом, переменные
pi
и
р2
указывают на один и тот же объект в памяти, как и переменная
о
(типа
object
). Учитывая, что
pi
,
р2
и
о
указывают на одно и то же местоположение в памяти, проверка эквивалентности дает положительный результат.
Хотя готовое поведение
System.Object
в ряде случаев может удовлетворять всем потребностям, довольно часто в специальных типах часть этих унаследованных методов переопределяется. В целях иллюстрации модифицируем класс
Person
, добавив свойства, которые представляют имя, фамилию и возраст лица; все они могут быть установлены с помощью специального конструктора:
// Не забывайте,
что класс Person расширяет Object.
class Person
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public int Age { get; set; }
public Person(string fName, string lName, int personAge)
{
FirstName = fName;
LastName = lName;
Age = personAge;
}
public Person{}
}
Переопределение метода System.Object.ToString
Многие создаваемые классы (и структуры) могут извлечь преимущества от переопределения метода
ToString
для возвращения строки с текстовым представлением текущего состояния экземпляра типа. Помимо прочего это полезно при отладке. То, как вы решите конструировать результирующую строку — дело личных предпочтений; однако рекомендуемый подход предусматривает отделение пар "имя-значение" друг от друга двоеточиями и помещение всей строки в квадратные скобки (такому принципу следуют многие типы из библиотек базовых классов .NET Core). Взгляните на следующую переопределенную версию
ToString
для класса
Person
:
public override string ToString
=> $"[First Name: {FirstName}; Last Name: {LastName};
Age:
{Age}]";
Приведенная реализация метода
ToString
довольно прямолинейна, потому что класс
Person
содержит всего три порции данных состояния. Тем не менее, всегда помните о том, что правильное переопределение
ToString
должно также учитывать любые данные, определенные выше в цепочке наследования.
При переопределении метода
ToString
для класса, расширяющего специальный базовый класс, первым делом необходимо получить возвращаемое значение
ToString
из родительского класса, используя ключевое слово
base
. После получения строковых данных родительского класса их можно дополнить специальной информацией производного класса.
Переопределение метода System.Object.Equals
Давайте также переопределим поведение метода
Object.Equals
, чтобы работать с семантикой на основе значений. Вспомните, что по умолчанию
Equals
возвращает
true
, только если два сравниваемых объекта ссылаются на один и тот же экземпляр объекта в памяти. Для класса
Person
может оказаться полезной такая реализация
Equals
, которая возвращает
true
, если две сравниваемые переменные содержат те же самые значения состояния (например, фамилию, имя и возраст).
Прежде всего, обратите внимание, что входной аргумент метода
Equals
имеет общий тип
System.Object
. В связи с этим первым делом необходимо удостовериться в том, что вызывающий код действительно передал экземпляр типа
Person
, и для дополнительной подстраховки проверить, что входной параметр не является ссылкой
null
.
После того, как вы установите, что вызывающий код передал выделенный экземпляр
Person
, один из подходов предусматривает реализацию метода
Equals
для сравнения поле за полем данных входного объекта с данными текущего объекта: