Книги онлайн и без регистрации » Разная литература » Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 93 94 95 96 97 98 99 100 101 ... 407
Перейти на страницу:
System.Object.

Console.WriteLine("ToString: {0}", p1.ToString());

Console.WriteLine("Hash code: {0}", p1.GetHashCode());

Console.WriteLine("Type: {0}", p1.GetType());

// Создать другие ссылки на pi.

Person p2 = p1;

object o = p2;

// Указывают ли ссылки на один и тот же объект в памяти?

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() для сравнения поле за полем данных входного объекта с данными текущего объекта:

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 в нашем примере), поскольку метод ToString() поддерживают все типы .NET. Еще лучше то, что больше не требуется выполнять проверку на предмет равенства свойство за свойством, т.к. теперь просто проверяются

1 ... 93 94 95 96 97 98 99 100 101 ... 407
Перейти на страницу:

Комментарии
Минимальная длина комментария - 20 знаков. В коментария нецензурная лексика и оскорбления ЗАПРЕЩЕНЫ! Уважайте себя и других!
Комментариев еще нет. Хотите быть первым?