netlib.narod.ru< Назад | Оглавление | Далее >

Экземпляры и наследование

Вполне возможно, что по прошествии некоторого времени пользования классом вы подумаете: «Этот класс очень хорош, но лучше, если б...» Если у вас есть исходный код класса, можете просто открыть его в редакторе, добавить новый метод, перекомпилировать и начать использовать. Но у вас может и не быть исходного кода. Возможно, что вам доступна только скомпилированная версия класса в виде DLL.

Может случиться и так: вы хотите, чтобы некоторые функции класса выполнялись иначе. Но класс уже используется в существующем виде другими приложениями и вполне их устраивает. Изменения требуются только для одного нового приложения, и вы предпочли бы не «засорять» код исходной версии.

В объектно-ориентированных языках, подобных С#, реализована возможность наследования (inheritance). Можно определить новый класс, основанный на существующем классе. Говорят, что вы наследуете (inherit) класс от существующего класса или создаете подкласс (subclass) существующего класса. В новом классе должны содержаться только новые данные и код. Все классы С# и .NET Framework являются наследниками класса Object или классов-наследников Object. Говорят также, что все классы в конечном счете являются производными от Object.

Давайте создадим новый класс DatePlus, унаследованный от Date. DatePlus будет обладать новым свойством DaysSincel600. Благодаря наличию этого свойства можно сделать, чтобы DatePlus вычислял разницу в днях между двумя датами.

Вот программа, в которой определен класс DatePlus.

CsDateConstructors.cs

  //----------------------------------------------------
  // CsDateInheritance.cs (C) 2001 by Charles Petzold
  //----------------------------------------------------
  using System;

  class CsDateInheritance
  {
      public static void Main()
      {
          DatePlus birth = new DatePlus(1953, 2, 2);
          DatePlus today = new DatePlus(2001, 8, 29);

          Console.WriteLine("Birthday = {0}", birth);
          Console.WriteLine("Today = " + today);
          Console.WriteLine("Days since birthday = {0}", today - birth);
      }
  }
  class DatePlus:Date
  {
      public DatePlus() {}
      public DatePlus(int year, int month, int day):base(year, month, day) {}

      public int DaySince1600
      {
          get
          {
              return 365 - (Year - 1606) +
                     (Year - 1597) / 4 -
                     (Year - 1601) / 100 +
                     (Year - 1601) / 400 + DayOfYear;
          }
      }
      public override string ToString()
      {
          string[] str = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }

          return String.Format("{0} {1} {2}", Day, str[Month - 1], Year);
      }
      public static int operator - (DatePlus date1, DatePlus date2)
      {
          return date1.DaysSince1600 - date2.DaysSince1600;
      }
  }

Эту программу необходимо компилировать вместе с файлом CsDateConstructors.cs — самой последней реализацией класса Date. Так как имеется два класса, содержащих метод Main, нужно указать компилятору, какой из классов, содержащих метод Main, использовать для входа в программу.

При компиляции в командной строке надо ввести:

  csc CsDateConstructors.cs CsDateInheritance.cs /main:CsDateInheritance

При этом нужно быть внимательным к регистру букв. Имена файлов можно вводить в любом регистре, но параметр /main обращается к классу, и регистр символов этого параметра должен соответствовать имени класса, определенному в файле. В случае Visual С# .NET нужно добавить CsDateConstructors.cs в проект CsDateInheritance. Для этого выберите в меню Project | Add Existing Item. При выборе файла CsDateConstructors.cs в диалоговом окне Add Existing Item щелкните по стрелке рядом с кнопкой Open и выберите Link File. Этот параметр позволяет избежать создания копии файла CsDateConstructors.cs, а также проблем, возникающих в случае, когда версия одной из копий этого файла изменилась, а другую при этом забыли обновить.

Обратите внимание на первую строку определения класса DatePlus:

  class DatePlus:Date

Это означает, что DatePlus наследуется от Date. Классу DatePlus не нужно выполнять в конструкторах специальных действий. Поэтому для него определен конструктор по умолчанию с пустым телом:

  public DatePlus() {}

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

Это правило неприменимо к конструкторам не по умолчанию. Конструктор с тремя параметрами не выполняет никаких специальных действий в DatePlus, но нужно определить его в классе и явно вызвать конструктор базового (base) класса, от которого наследуется DatePlus, — класса Date. Определение конструктора не по умолчанию имеет такой синтаксис:

  public DatePlus(int year, int month, int day):base(year, month, day) {}

Так как конструктор не делает в классе DatePlus ничего специфического, его тело пусто.

В классе DatePlus, кроме свойства DaysSincel600, реализованы две изящные возможности. Во-первых, в DatePlus определен оператор вычитания (–) для объектов этого класса. Это называется перегрузкой (overloading) оператора. Обычно оператор вычитания определяется только для чисел, но здесь он хорошо подходит и для работы с датами. Тело этого перегруженного оператора довольно простое: в нем просто вычитаются DaysSincel600 двух дат.

Например, если определить два объекта DatePlus:

  DatePlus birth = new DatePlus(1953, 2, 2);
  DatePlus today = new DatePlus(2001, 8, 29);

то можно посчитать разницу в днях между этими датами при помощи выражения

  today - birth

Заметьте: я не реализовал в этом классе перегруженный оператор сложения. Не имеет смысла складывать друг с другом две даты. Однако, я мог бы реализовать сложение даты и числа, результатом которого является новая дата. Но тогда потребовалось бы писать код, чтобы преобразовать новое значение DaysSincel600 в дату. Зато довольно легко реализовать операторы сравнения (<, >, <= и >=).

Во-вторых, я уже упоминал, что все объекты в конечном счете наследуются от Object. В классе Object реализован метод ToString, предназначенный для преобразования объекта в читабельную текстовую строку. Мы уже использовали ToString. При конкатенации числовой переменной с текстовой строкой автоматически вызывается метод ToString этой переменной. При передаче объекта в метод ConsoleWriteLine также вызывается метод ToString объекта.

Но по умолчанию метод ToString класса Object возвращает имя класса, например строку «DatePlus». Ничего страшного: ведь любой класс, происходящий от Object (т.е. любой класс С#) может переопределить (override) метод ToString класса Object своим собственным. В классе DatePlus реализован собственный метод ToString, использующий статический метод String.Format для преобразования даты в текстовую строку. Стало возможным вызвать метод Console.WriteLine, указав в качестве параметра объект DatePlus, и показать форматированную дату. Вывод программы CsDateInheritance выглядит так

  Birthday = 2 Feb 1953
  Today = 29 Aug 2001
  Days since birthday = 17740

Теперь мы готовы более подробно рассмотреть модификаторы доступа. Если поле, свойство или метод определены как private, они видимы и доступны только внутри класса. Если поле, свойство или метод определены как public, они видимы и доступны в других классах. Если поле, свойство или метод определены как protected, они видимы и доступны только внутри класса и любого класса, унаследованного от этого класса.

Метод ToString класса Object определен с модификатором virtual. Метод, определенный как virtual, может переопределяться наследниками класса. При переопределении метода используется модификатор override, показывающий, что метод заменяется собственной версией, реализованной в этом классе. Модификатор override необходим, чтобы нельзя было ошибиться, случайно переопределив виртуальный метод, когда этого не планировалось.

Кроме того, в классах можно переопределять методы, не объявленные как virtual. В этом случае новый метод должен содержать модификатор new.

Кроме ToString, класс Object содержит несколько других методов, в том числе GetType. GetType возвращает объект типа Туре. Туре — это класс, определенный в пространстве имен System, позволяющий получить информацию об объекте, в частности, о его методах, свойствах и полях. Оператор С# typeof также возвращает объект типа Туре. Отличие между ними в том, что GetType применяется к объекту, a typeof — к классу. В методе Main программы CsDataInheritance результатом выражения

  today.GetType(} == typeof(DatePlus)

будет true.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz