Книги онлайн и без регистрации » Разная литература » Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 353 354 355 356 357 358 359 360 361 ... 415
Перейти на страницу:
потомок, продолжающий дело отца, только уже на новом уровне.

Класс-потомок наследует все возможности родительского класса — все поля и все методы, открытую и закрытую часть класса. Правда, не ко всем полям и методам класса возможен прямой доступ потомка. Поля и методы родительского класса, снабженные атрибутом private, хотя и наследуются, но по-прежнему остаются закрытыми, и методы, создаваемые потомком, не могут к ним обращаться напрямую, а только через методы, наследованные от родителя. Единственное, что не наследует потомок — это конструкторы родительского класса. Конструкторы потомок должен создавать сам. В этом есть некоторая разумная идея, и я позже поясню ее суть.

Рассмотрим класс, названный Found, играющий роль родительского класса. У него есть обычные поля, конструкторы и методы, один из которых снабжен новым модификатором virtual, ранее не появлявшимся в классах, другой — модификатором override:

public class Found

{

   //поля

   protected string name;

   protected int credit;

   public Found()

   {

   }

   public Found(string name, int sum)

   {

       this.name = name; credit = sum;

   }

   public virtual void VirtMethodO {

       Console.WriteLine ("Отец: " + this.ToString());

   }

   public override string ToString ()

   {

        return(String.Format("поля: name = {0}, credit = {1}", name, credit));

   }

   public void NonVirtMethod()

   {

        Console.WriteLine ("Мать: " + this.ToString());

   }

   public void Analysis()

   {

        Console.WriteLine ("Простой анализ");

   }

   public void Work()

   }

        VirtMethod();

        NonVirtMethod();

        Analysis ();

    }

}

Заметьте, класс Found, как и все классы, по умолчанию является наследником класса Object, его потомки наследуют методы этого класса уже не напрямую, а через методы родителя, который мог переопределить методы класса Object, в частности, класс Found переопределил метод Tostring, задав собственную реализацию возвращаемой методом строки, которая связывается с объектами класса. Как часто делается, в этой строке отображаются значения полей объекта данного класса. На переопределение родительского метода Tostring указывает модификатор метода override.

Класс Found закрыл свои поля для клиентов, но открыл для потомков, снабдив их модификатором доступа protected.

Создадим теперь класс Derived — потомка класса Found, в простейшем случае объявление класса может выглядеть так:

public class Derived: Found

}

}

Тело класса Derived пусто, но это вовсе не значит, что объекты этого класса не имеют полей и методов: они "являются" объектами класса Found, наследуя все его поля и методы (кроме конструктора) и поэтому могут делать все, что могут делать объекты родительского класса.

Вот пример работы с объектами родительского и производного класса:

public void TestFoundDerived()

{

   Found bs = new Found ("father", 777);

   Console.WriteLine("Объект bs вызывает методы базового класса");

   bs.VirtMethod();

   bs.NonVirtMethod ();

   bs.Analysis(); bs.Work();

   Derived der = new Derived ();

   Console.WriteLine("Объект der вызывает методы класса потомка");

   der.VirtMethod();

   der.NonVirtMethod();

   der.Analysis();

   der.Work();

}

Результаты работы этой процедуры показаны на рис. 18.2.

Рис. 18.2. Объект потомка наследует поля и методы родителя

В чем отличие работы объектов bs и der? Поскольку класс-потомок Derived ничего самостоятельно не определял, то он наследовал все поля и методы у своего родителя, за исключением конструкторов. У этого класса имеется собственный конструктор без аргументов, задаваемый по умолчанию. При создании объекта der вызывался его собственный конструктор по умолчанию, инициализирующий поля класса значениями по умолчанию. Об особенностях работы конструкторов потомков скажу чуть позже, сейчас же упомяну лишь, что конструктор по умолчанию потомка вызывает конструктор без аргументов своего родителя, поэтому для успеха работы родитель должен иметь такой конструктор. Заметьте, поскольку родитель не знает, какие у него могут быть потомки, то желательно конструктор без аргументов включать в число конструкторов класса, как это сделано для класса Found.

Добавление полей потомком

Ничего не делающий самостоятельно потомок не эффективен, от него мало проку. Что же может делать потомок? Прежде всего, он может добавить новые свойства — поля класса. Заметьте, потомок не может ни отменить, ни изменить модификаторы или типы полей, наследованных от родителя — он может только добавить собственные поля.

Модифицируем наш класс Derived. Пусть он добавляет новое поле класса, закрытое для клиентов этого класса, но открытое для его потомков:

protected int debet;

Напомню, хорошей стратегией является стратегия "ничего не скрывать от потомков". Какой родитель знает, что именно из сделанного им может понадобиться потомкам?

Конструкторы родителей и потомков

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

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

Вызов конструктора родителя происходит не в теле конструктора, а в заголовке, пока еще не создан объект класса. Для вызова конструктора используется ключевое слово base, именующее родительский класс. Как это делается, покажу на примере конструкторов класса Derived:

public Derived () {}

public Derived(string name, int cred, int deb):base (name,cred)

{

    debet = deb;

}

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

1 ... 353 354 355 356 357 358 359 360 361 ... 415
Перейти на страницу:

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