Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
26
Добавлен:
29.02.2016
Размер:
69.59 Кб
Скачать

Добавление в иерархию конструкторов с параметрами

Начнем с конструктора с одним параметром для GenericCustomer, который обеспечит создание экземпляров заказчиков только при условии указания имени:

abstract class GenericCustomer

{

private string name;

public GenericCustomer(string name)

{

this.name = name;

}

Пока все хорошо. Однако, как уже было сказано, это приведет к ошибке компиля­ции, когда компилятор попытается создать конструктор по умолчанию для любого про­изводного класса, потому что сгенерированный компилятором конструктор по умол­чанию для Nevermore60Customer попытается вызвать конструктор без параметров GenericCustomer, а класс GenericCustomer не предоставляет такого конструктора. Во избежание такой ошибки компиляции, потребуется предусмотреть собственный конструк­тор для производного класса:

class Nevermore60Customer:GenericCustomer

{

private uint highCostMinutesUsed;

public Nevermore60Customer(string name):base(name)

{

}

Теперь создание экземпляров объектов Nevermore60Customer возможно только при условии указания строки имени заказчика, что и требовалось в любом случае. Интересно, что конструктор Nevermore60Customer делает с этой строкой. Вспомните, что он не мо­жет инициализировать поле name сам по себе, поскольку не имеет доступа к приватным полям базового класса. Вместо этого он передает строку имени на обработку конструкто­ру базового класса GenericCustomer. Для этого указывается, что первым будет выполнен конструктор базового класса, принимающий имя в параметре. Ничего другого помимо это­го конструктор не делает.

Теперь посмотрим, что произойдет, если в иерархии классов будут присутствовать раз­личные перегрузки конструкторов. Например, предположим, что клиенты, подключаемые по тарифному плану Nevermore60, могут обращаться к MortimerPhones по совету друга, участвующего в программе "подключи друга и получи скидку". Это значит, что при конст­руировании Nevermore60Customer может понадобиться вместе с именем клиента пере­давать имя друга, который его привел. В реальной жизни при этом конструктор должен будет делать что-то сложное с именем (например, вычислять скидку для друга), но пока ограничимся только сохранением его имени в отдельном поле.

Теперь определение Nevermore60Customer будет выглядеть следующим образом:

class Nevermore60Customer: GenericCustomer

{

public Nevermore60Customer(string name, string referrerName)

: base(name)

{

this.referrerName = referrerName;

}

private string referrerName;

private uint highCostMinutesUsed;

Этот конструктор принимает имя и передает его на обработку конструктору GenericCustomer. При этом referrerName  переменная, ответственность за которую несет класс Nevermore60Customer, потому упомянутый параметр обрабатывается в основ­ном теле конструктора.

Однако не все экземпляры Nevermore60Customer будут иметь друга, поэтому нам по-прежнему необходим конструктор, который не требует второго параметра (или конст­руктор, присваивающий ему значение по умолчанию). Фактически, если друга нет, то зна­чение refererName должно будет установлено равным "<None>" за счет использования следующего конструктора с одним параметром:

public Nevermore60Customer(string name)

: this(name, "<None>")

{

}

Теперь мы имеем корректно настроенный конструктор. Поучительно будет рассмот­реть цепочку событий, которые произойдут при выполнении строки вроде следующей:

GenericCustomer customer = new Nevermore60Customer("Arabel Jones");

Компилятор видит, что ему нужен конструктор с одним параметром, принимающий одну строку, поэтому он идентифицирует тот, который вы определили последним:

public Nevermore60Customer(string Name)

: this(Name, "<None>")

Этот конструктор будет вызван при создании экземпляра customer. Он немедленно передаст управление соответствующему конструктору Nevermore60Customer с двумя пара­метрами, переслав ему значения "Arabel Jones" и "<None>". Посмотрев на код этого кон­структора, вы увидите, что тот, в свою очередь, немедленно передает управление конструк­тору GenericCustomer с одним параметром, значение которого в данном случае  "Arabel Jones", а последний передает управление конструктору по умолчанию System.Object. Только здесь начнется, собственно, вся работа конструкторов. Сначала выполнится кон­структор System.Object. Дальше отработает конструктор GenericCustomer, который инициализирует поле name. После этого конструктор Nevermore6OCustomer с двумя па­раметрами получит управление обратно и выполнит инициализацию поля referrerName значением "<None>". И, наконец, управление получит конструктор Nevermore60Customer с одним параметром, который больше ничего не делает.

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

Модификаторы

Ранее вы уже сталкивались с некоторыми из так называемых модификаторов — клю­чевыми словами, которые могут быть применены к типу или члену. Модификаторы мо­гут указывать видимость метода, как, например public или private, или же их природу, например, virtual или abstract. В языке С# определено множество модификаторов, и сейчас стоит потратить некоторое время на ознакомление с их полным списком.

Модификаторы видимости

Модификаторы видимости указывают, какие другие единицы кода могут видеть эле­мент (табл. 4.1).

Таблица 4.1. Модификаторы видимости

Модификатор

К чему относится

Описание

public

К любым типам или членам

Элемент виден в любом другом коде

protected

К любому члену типа, а также к любому вложенному типу

Элемент видим только любому производ­ному типу

internal

К любым типам или членам

Элемент видим только в пределах включаю­щей его сборки

private

К любому члену типа, а также к любому вложенному типу

Элемент видим только в пределах типа, ко­торому он принадлежит

protected internal

К любому члену типа, а также к любому вложенному типу

Элемент видим только в пределах включаю­щей его сборки, а также в любом коде внут­ри производного типа

Следует обратить внимание, что определения типа могут быть общедоступными или приватны­ми в зависимости от того, хотите ли вы обеспечить его видимость извне сборки.

public class MyClass

{

// и т.д.

Указывать модификаторы protected, private или protected internal для типов нельзя, поскольку эти уровни видимости не имеют смысла для типа, находящегося в про­странстве имен. Это значит, что они могут относиться только к членам. Однако возможно создавать вложенные типы (т.е. типы, содержащиеся внутри других типов) с такой види­мостью, поскольку в этом случае типы имеют статус члена. Таким образом, приведенный ниже код вполне корректен:

public class OuterClass

{

protected class InnerClass

{

// и т.д.

}

// и т.д.

}

Если есть вложенный тип, он всегда может иметь доступ ко всем членам внешнего типа. Таким образом, в последнем примере любой код внутри InnerClass всегда имеет доступ ко всем членам OuterClass, даже если они объявлены как private.

Другие модификаторы

Модификаторы, перечисленные в табл. 4.2, могут быть применены к членам типов и характеризуются различным использованием. Некоторые из них также имеет смысл ис­пользовать для типов.

Таблица 4.2. Другие модификаторы

Модификатор

К чему относится

Описание

new

К функциям-членам

Член скрывает унаследованный член с той же сигнатурой

static

Только к классам и функциям-членам

Член не связан с конкретным экземпляром класса

virtual

К любым типам или членам

Член может быть переопределен в классах- наследниках

abstract

Только к функциям-членам

Виртуальный член, определяющий сигнатуру, но не предоставляющий реализации

override

Только к функциям-членам

Член переопределяет унаследованный вирту­альный или абстрактный член базового класса

sealed

К классам, методам и свойствам

Для классов означает, что от таких классов нельзя наследовать. Для свойств и методов 

член переопределяет унаследованный вирту­альный член, но не может быть переопределен ни одним членом производных классов. Должен применяться в сочетании с override

extern

Только к статическим методам [Dllimport]

Член реализован внешне, на другом языке

Соседние файлы в папке 04 Наследование