Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Вызов одних конструкторов из другихСодержание книги
Поиск на нашем сайте
Иногда вы можете столкнуться с ситуацией, когда у класса есть несколько конструкторов, возможно, с разными необязательными параметрами, код которых делает нечто общее. Например, рассмотрим код: 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; просмотров: 223; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.16.130.96 (0.007 с.) |