Вызов одних конструкторов из других 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Вызов одних конструкторов из других



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

class Car

{

private string description;

private uint nWheels;

public Car(string description, uint nWheels)

{

this.description = description;

this.nWheels = nWheels;

}

public Car(string description)

{

this.description = description;

this.nWheels = 4;

}

// и т.д.

Оба конструктора инициализируют одни и те же поля. Ясно, что лучше поместить весь код в одном месте. В С# предусмотрен для этого специальный синтаксис, известный как инициализатор конструктора.

class Car

{

private string description;

private uint nWheels;

public Car(string description, uint nWheels)

{

this.description = description;

this.nWheels = nWheels;

}

public Car(string description): this(description, 4)

{

}

// и т.д.

В данном контексте ключевое слово this просто означает вызов подходящего по сиг­натуре конструктора. Обратите внимание, что любой инициализатор конструктора вызы­вается перед выполнением тела конструктора.

Скажем, имеется такой код:

Car myCar = new Car("Proton Persona");

В этом примере конструктор с двумя параметрами выполняется перед любым кодом, содержащимся в теле конструктора с одним параметром (хотя в данном конкретном случае это не имеет значения, потому что никакого дополнительного кода в теле конструктора с одним параметром нет).

Инициализатор конструктора С# может содержать либо один вызов другого конструк­тора того же класса (используя приведенный выше синтаксис), либо один вызов конструк­тора непосредственного базового класса (с тем же синтаксисом, но с ключевым словом base вместо this). В инициализатор нельзя помещать более одного вызова.

Поля readonly

Концепция константы как переменной, содержащей значение, которое не может быть изменено - это нечто, что С# разделяет с большинством других языков программирова­ния. Однако константы не обязательно отвечают всем требованиям. Иногда может пона­добиться переменная, которая не должна быть изменена, но чье значение не известно до момента запуска программы. Для таких случаев в С# предусмотрен другой тип перемен­ных: поля readonly.

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

Предположим, что имеется MDI-программа (Multiple Document Interface - многодо­кументный интерфейс), которая редактирует документы, но по причинам лицензионной политики вы хотите ограничить количество документов, открытых одновременно. Теперь предположим, что вы продаете различные версии программы и хотите дать возможность пользователю обновлять свои лицензии с тем, чтобы открывать одновременно больше документов. Это означает, что вы можете жестко закодировать максимальное количество документов в исходном коде. Вероятно, понадобится поле, представляющее максимальное число. При каждом запуске программы это поле должно быть прочитано - возможно, из системного реестра или какого-то файлового хранилища. В результате код может выгля­деть примерно так:

public class DocumentEditor

{

public static readonly uint MaxDocuments;

static DocumentEditor ()

{

MaxDocuments = DoSomethingToFindOutMaxNumber();

}

В данном случае поле статическое, поскольку максимальное количество докумен­тов должно быть сохранено лишь однажды при каждом запуске экземпляра програм­мы. Поэтому оно инициализируется в статическом конструкторе. Если же имеется поле readonly, принадлежащее экземпляру, то оно должно инициализироваться в конструкто­ре (конструкторах) экземпляра. Например: предположительно каждый редактируемый документ имеет дагу создания, которая не должна­ изменяться пользователем (потому что это означает изменение прошлого). Кроме того, это поле является общедоступным - обычно незачем объявлять readonly-поля приватными, поскольку по определению они не могут быть модифицированы извне (то же самое касается констант).

Как уже упоминалось, даты представлены классом System.DateTime. В следующем коде используется конструктор System.DateTime, принимающий три параметра (год, месяц и день месяца; подробнее об остальных конструкторах DateTime можно узнать в документа­ции MSDN).

public class Document

{

public readonly DateTime CreationDate;

public Document()

{

// Читаем дату создания файла. Предположим, что в результате

// получаем 1 января 2010 г., но вообще даты могут быть разными

// для различных экземпляров класса.

CreationDate = new DateTime(2008, 1, 1);

}

}

В последних двух фрагментах кода CreationDate и MaxDocuments трактуются как лют бое другое поле, за исключением того, что поскольку они доступны только для чтения, им нельзя присваивать значения вне конструкторов.

void SomeMethod()

{

MaxDocuments = 10;

// Здесь - ошибка компиляции. MaxDocuments объявлен как readonly.

}

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

Анонимные типы

В разделе 6.2 обсуждалось ключевое слово var, ссылающееся на неявно типизированные переменные. При использовании его вместе с ключевым словом new можно создавать анонимные типы. Анонимный тип -это просто безымянный класс, унаследованный от Object. Определение класса выводится компилятором из его инициализатора, как и в слу­чае с неявно типизированными переменными.

Если нужен объект, содержащий имя, отчество и фамилию лица, то его объявление мо­жет выглядеть так:

var captain = new {FirstName = "James", MiddleName = "T",

LastName = "Kirk"};

Это создаст объект со свойствами FirstName, MiddleName и LastName. И если вы соз­дадите другой объект, который выглядит следующим образом:

var doctor = new {FirstName = "Leonard", MiddleName = "",

LastName = "McCoy"};

то типы captain и doctor будут одинаковыми. Так, например, можно установить captain = doctor.

Если устанавливаемое в объект значение поступает от другого объекта, то инициали­затор может быть сокращен. Если уже имеется класс, содержащий свойства FirstName, MiddleName и LastName, и есть экземпляр этого класса с именем экземпляра person, то объект captain может быть инициализирован, как показано ниже:

var captain = new (person.FirstName, person.MidleName,

person.LastName};

Имена свойств из объекта person будут спроецированы на новый объект по имени captain. Таким образом, объект по имени captain будет иметь свойства FirstName, MiddleName и LastName.

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

Структуры

До сих пор вы видели, насколько хорошо классы позволяют обеспечить инкапсуляцию объектов в программах. Также было показано, как они сохраняются в куче, обеспечивая гибкость в отношении времени жизни данных, однако с некоторыми затратами произво­дительности. Эти затраты производительности - лишь небольшая плата за оптимизацию управляемой кучи. Однако в некоторых ситуациях все, что нужно - это маленькая струк­тура данных. В этом случае класс предоставляет больше функциональности, чем требует­ся, и из соображений производительности имеет смысл отдать предпочтение структуре. Рассмотрим пример:

class Dimensions

{

public double Length;

public double Width;

}

Этот код определяет класс Dimensions, который просто сохраняет длину и ширину элемента. Возможно, вы пишете программу для расстановки мебели, которая позволит лю­дям экспериментировать на компьютере с разными вариантами расположения мебели, и требуется хранить размеры каждой единицы мебели. На первый взгляд, кажется, что здесь нарушены правила хорошего программного дизайна - тем, что поля общедоступны, но дело в том, что в данном случае вовсе не нужны все возможности, предлагаемые/классами. Все, что необходимо - два числа, которые удобнее хранить вместе, чем по отдельности. Нет необходимости во множестве методов, не требуется наследование от этого класса, и не нужны никакие возможные проблемы для.NET, связанные с обслуживанием кучи — с соответствующим влиянием на производительность - для того, чтобы просто сохранить два числа.

Как упоминалось ранее в этой главе, единственное, что следует изменить в коде, чтобы сделать этот тип структурой вместо класса - просто заменить ключевое слово class на struct:

struct Dimensions

{

public double Length;

public double Width;

}

Определение функций для структур выглядит точно так же, как определение функций для классов. В следующем коде показана структура с конструктором и свойством:

struct Dimensions

{

public double Length;

public double Width;

Dimensions(double length, double width)

{

Length=length;

Width=width;

}

public int Diagonal

{

get

{

return Math.Sqrt(Length*Length + Width*Width);

}

}

}

Структуры - это типы значений, а не ссылочные типы. Это значит, что они либо со­храняются в стеке, либо являются встроенными (последнее - если они являются частью другого объекта, хранимого в куче), и имеют те же ограничения времени жизни, что и простые типы данных.

- Структуры не поддерживают наследование.

- Имеется некоторое отличие в работе конструкторов структур. В частности, компи­лятор всегда генерирует конструктор по умолчанию без параметров, который пере­определить невозможно.

- Для структур можно указывать то, как поля размещаются в памяти (это рассматрива­ется в разделе 6.14, посвященной, помимо прочего, атрибутам).

Поскольку структуры на самом деле предназначены для группирования данных, в большинстве случаев все их поля объявляются общедоступными. Строго говоря, это про­тиворечит рекомендациям по написанию кода.NET. Согласно Microsoft, поля (кроме кон­стантных) всегда должны быть приватными и помещенными в оболочки общедоступных свойств. Однако что касается простых структур, многие разработчики считают примене­ние общедоступных полей допустимой практикой программирования.

В следующих разделах некоторые отличия между классами и структурами рассматрива­ются более подробно.



Поделиться:


Последнее изменение этой страницы: 2016-12-27; просмотров: 188; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.22.249.220 (0.017 с.)