- •Структуры и классы
- •Наследование реализации
- •Виртуальные методы
- •Сокрытие методов
- •Вызов базовых версий функций
- •Абстрактные классы и функции
- •Запечатанные классы и методы
- •Конструкторы производных классов
- •Добавление в иерархию конструктора
- •Добавление в иерархию конструкторов с параметрами
- •Интерфейсы
- •IDisposable — сравнительно простой интерфейс, потому что в нем определен только один метод. Большинство интерфейсов содержат гораздо большее количество методов.
- •Определение и реализация интерфейсов
- •Производные интерфейсы
Добавление в иерархию конструктора
Для начала возьмем простейший случай и посмотрим, что произойдет, если где-то в иерархии заменить конструктор по умолчанию другим конструктором, не имеющим параметров. Предположим, вы решили, что имя всегда должно быть инициировано строкой "<no name>" вместо ссылки на null. Модифицируем код GenericCustomer следующим образом:
public abstract class GenericCustomer
{
private string name;
public GenericCustomer()
:based //эту строку можно пропустить без влияния
//на скомпилированный код
{
name = "<no name>";
}
}
После добавления этого кода все будет работать хорошо. У Nevermore60Customer по-прежнему имеется конструктор по умолчанию, поэтому описанная ранее последовательность событий происходит, как и раньше, за исключением того, что компилятор будет вызывать пользовательский конструктор GenericCustomer вместо конструктора по умолчанию, поэтому поле name всегда будет инициализировано строкой "<no name>", что и требовалось.
Обратите внимание, что перед запуском вашего конструктора GenericCustomer добавился вызов конструктора базового класса с применением синтаксиса, который использовался ранее во время обсуждения, как получить различные перегрузки конструкторов для вызова друг друга. Единственное отличие состоит в том, что на этот раз присутствует ключевое слово base вместо this, указывающее на то, что вызывается конструктор базового класса, а не другой конструктор этого же класса. В скобках после ключевого слова base не задано никаких параметров; это важно, поскольку означает, что никакие параметры базовому конструктору не передаются, поэтому компилятор будет искать для вызова конструктор без параметров. В результате всего этого компилятор вставит код вызова конструктора System.Object, что произошло бы по умолчанию в любом случае.
На самом деле, эту строку кода можно опустить и написать следующее (как делалось с множеством конструкторов, рассмотренных до сих пор в этой главе):
public GenericCustomer()
{
name = "<no name>";
}
Если компилятор не обнаруживает никаких ссылок на другой конструктор перед открытием "фигурной скобки, то предполагает, что вы намерены вызвать конструктор базового класса; так работает и конструктор по умолчанию.
Ключевые слова base и this единственные ключевые слова, которые допускаются в' этой строке для вызова другого конструктора. Все остальное приведет к ошибке во время компиляции. Также следует отметить, что может быть указан только один другой конструктор.
До сих пор код работал хорошо. Одним из способов испортить все это путешествие по иерархии конструкторов может быть объявление конструктора с модификатором private:
i
private GenericCustomer() {
name = "<no name>";
Если вы попробуете сделать так, то столкнетесь с интересной ошибкой компиляции, которая может действительно поразить, если вы не понимаете, как вызываются конструкторы в иерархии классов:
‘Wrox.ProCSharp.GenericCustomer.GenericCustomer()’ is inaccessible due to its protection level
'Wrox.ProCSharp.GenericCustomer.GenericCustomer()' не доступен из-за его уровня защиты
Интересно, что эта ошибка появится не в классе GenericCustomer, а в классе-наследнике Nevermore60Customer. А происходит здесь вот что: компилятор пытается сгенерировать конструктор по умолчанию для Nevermore60Customer, но не может этого сделать, потому что конструктор по умолчанию должен вызвать конструктор без параметров базового класса GenericCustomer. Объявив этот конструктор как private, вы тем самым сделали его недоступным для классов-наследников. Похожая ошибка случится, если определить конструктор GenericCustomer, принимающий параметры, но не определить конструктора без параметров. В таком случае компилятор не генерирует конструктор по умолчанию для GenericCustomer, поэтому, когда он попытается сгенерировать конструктор по умолчанию для любого класса-наследника, он не сможет этого сделать, потому что не найдет конструктора базового класса без параметров. Чтобы обойти это, в производный класс понадобится добавить собственные конструкторы, даже если делать в них ничего не нужно лишь только для того, чтобы компилятор не пытался генерировать для класса конструктор по умолчанию.
Теперь, обладая необходимыми теоретическими знаниями, вы готовы к изучению примера аккуратного добавления конструкторов в иерархию классов. В следующем разделе мы начнем добавлять конструкторы с параметрами в пример программы МогtimeгPhones.