Лекция-05_Пр-е.ppt
- Количество слайдов: 25
Классы, структуры, методы, модификаторы доступа (продолжение) Лекция 02. 10. 12 г. 1
Члены класса Что может размещаться внутри класса: «Содержимое» класса Вложенные типы Члены класса Данные-члены класса Поля Неизменяемые поля Функции-члены класса Свойства Методы Конструкторы экземпляров Непараметризованные Параметризованные Константы Конструктор класса Перегруженные операторы События Финализатор Перегруженные операторы преобразования типа Лекция 02. 10. 12 г. 2
Члены класса Данные и функции, объявленные внутри класса, являются членами класса. В дополнение к членам, классы могут содержать в себе вложенные типы (например, другие классы). Члены класса могут быть объявлены как public (общедоступные, или открытые - в этом случае они непосредственно доступны извне класса), private (приватные, или закрытые - в этом случае они видимы только коду этого же класса) и protected (защищенные, что означает видимость члена самому классу и всем его наследникам). К данным – членам класса относятся: поля, константы, события. Данные-члены класса могут быть: ØСтатическими (ассоциированными с классом в целом) и в этом случае они сопровождаются ключевым словом static. ØОтносящимися к экземпляру (когда каждый экземпляр класса содержит собственную копию данных). Лекция 02. 10. 12 г. 3
Данные-члены класса (поля, константы, события) Поля – это переменные, ассоциированные с классом. Константы могут быть ассоциированы с классом так же, как переменные. Константа объявляется с помощью ключевого слова const. События – это члены класса, позволяющие объекту уведомлять вызывающий код о совершении какого-либо события, например, произошло изменение свойства класса либо некоторое взаимодействие с пользователем. Клиент может иметь код, называемый обработчик событий, реагирующий на них. При создании экземпляра объекта можно обращаться к полям, используя синтаксис Объект. Имя. Поля, как показано в следующем примере: Motorcycle c = new Motorcycle(5); с. driver. Intensity = 7; Неизменяемые поля (поля только для чтения), как и константы, не могут быть изменены после начального присваивания. Однако, в отличие от констант, значение, присваиваемое такому полю, может быть определено во время выполнения в контексте конструктора (но нигде более). class My. Math. Class { public readonly double PI; public My. Math. Class(){ PI = 3. 14; } } Лекция 02. 10. 12 г. 4
Функции-члены класса обеспечивают требуемую функциональность для манипулирования данными класса. Они включают: методы, свойства, конструкторы, финализаторы, операции и индексы. Методы (method) – это функции, ассоциированные с определенным классом. Они могут быть либо методами экземпляра, работающими с конкретным экземпляром класса, либо статическими методами, которые представляют более общую функциональность, не требующую наличия экземпляра класса (например, метод Console. Write. Line()). Свойства (property) – это наборы функций, которые могут быть доступны клиенту таким же способом, как общедоступные поля класса (т. е. ведут себя как поля). В языке С# имеется специальный синтаксис для реализации чтения и записи свойств для классов, поэтому не нужно писать собственные методы с именами, начинающимися на set и get. Конструкторы (constructor) — это специальные функции, вызываемые автоматически при инициализации объекта. Их имена совпадают с именами классов, которым они принадлежат, и они не имеют типа возврата. Конструкторы полезны для инициализации полей класса. Различают конструкторы экземпляров класса и конструкторы класса. Лекция 02. 10. 12 г. 5
Функции-члены класса Финализаторы (finalizer) похожи на конструкторы, но вызываются, когда CLR определяет, что объект больше не нужен. Они имеют то же имя, что и класс, но с предшествующим символом «тильда» (~). Финализаторы используются реже, чем их ближайший аналог – деструкторы языка С++, потому что CLR выполняет сборку мусора автоматически. К тому же невозможно предсказать точно, когда будет вызван финализатор. Перегруженные операторы (operator) – позволяют специфицировать, как существующие операции будут работать с пользовательскими классами. Индексаторы (indexer) позволяют индексировать объекты таким же способом, как массив или коллекцию. Лекция 02. 10. 12 г. 6
Анонимные типы Анонимный тип – это безымянный класс, унаследованный от Object. Определение класса выводится компилятором из его инициализатора, как и в случае с неявно типизированными переменными. Пример: var captain = new {First. Name = "James", Middle. Name = "T", Last. Name = "Kirk”}; В результате будет создан объект со свойствами First. Name, Middle. Name и Last. Name. Если создать другой объект: var doctor = new {First. Name = "Leonard", Middle. Name = "", Last. Name = "Mc. Coy”}; то типы объектов captain и doctor будут одинаковы. Так, например, можно установить captain = doctor. Если уже есть класс, содержащий свойства First. Name, Middle. Name и Last. Name, и есть экземпляр этого класса с именем экземпляра person, то объект captain может быть инициализирован, как показано ниже: var captain = new {person. First. Name, person. Midle. Name, person. Last. Name}; Действительное имя типа рассмотренных объектов неизвестно. Компилятор создает имя этого типа, и только компилятор может его использовать. Лекция 02. 10. 12 г. 7
Структуры Рассмотрим пример определения класса: class Dimensions { public double Length, Width; } Предположим, что данный класс используется в самых простых задачах. Открытые поля – это не очень хорошее решение, но в данном случае нет необходимости их скрывать, потому что класс будет использоваться внутри других классов. Нет необходимости во множестве методов, не требуется наследование от этого класса, и не нужны никакие возможные проблемы для. Net, связанные обслуживанием «кучи» – с соответствующим влиянием на производительность – для того, чтобы просто сохранить два числа. Единственное, что нужно изменить в этом коде, чтобы сделать этот тип структурой вместо класса – это просто заменить ключевое слово class на struct: struct Dimensions { public double Length, Width; } Лекция 02. 10. 12 г. 8
Структуры Определение функций для структур выглядит точно так же, как определение функций для классов. Следующий код демонстрирует структуру с конструктором и свойством: struct Dimensions { public double Length, Width; Dimensions (double length, double width) { // конструктор Length=length; Width=width; } public double Diagonal { // свойство get { return Math. Sqrt(Length*Length + Width*Width); } } } Поскольку структуры являются типами значений, операция new с ними работает несколько иначе, чем с классами и другими ссылочными типами. Вместо того чтобы выделять память в куче, операция new просто вызывает подходящий конструктор, инициализируя поля в соответствии с переданными ему параметрами. Для структур также можно написать: Dimensions point; point. Length = 3; point. Width = 6; Лекция 02. 10. 12 г. 9
Структуры Для структур объявление переменной на самом деле выделяет пространство в стеке для структуры, поэтому она сразу же готова к присвоению значений полям. Однако, что следующий код вызовет ошибку компиляции с сообщением о том, что используется неинициализированная переменная: Dimensions point; double D = point. Length; Структуры следуют тем же правилам, что и все другие типы данных: все должно быть инициализировано перед использованием. Структура рассматривается как полностью инициализированная, когда для ее создания была вызвана операция new или же когда всем ее полям индивидуально были присвоены значения. Структура, определенная как поле класса, инициализируется автоматическим обнулением при инициализации ее включающего объекта. Лекция 02. 10. 12 г. 10
Структуры и наследование. Конструкторы структур Структуры не предназначены для наследования и наследовать от структуры нельзя. Единственное исключение состоит в том, что структуры, как и все другие типы в С#, в конечном итоге наследуются от класса System. Object. Поэтому структуры имеют доступ к методам класса System. Object. Действительная цепочка наследования, которая приводит к структуре, состоит в том, что каждая структура наследуется от класса System. Value. Type, который, в свою очередь, наследуется от System. Object. Value. Type, который не добавляет новых членов к Object, но представляет реализацию ряда из них более подходящим для структур способом. Можно объявлять конструкторы структур точно так же, как это делается для классов, за исключением того, что не разрешается определять конструкторы без параметров. Конструктор по умолчанию, который инициализирует все поля нулевыми значениями, всегда присутствует неявно, даже если применяются другие конструкторы с параметрами. Также невозможно обойти конструктор по умолчанию, определяя начальные значения полей. Следующий код вызовет ошибку компиляции: struct Dimensions { public double Length = 1, Width = 2; // Ошибка. Начальные значения не разрешены } Лекция 02. 10. 12 г. 11
Частичные классы Ключевое слово partial позволяет определить класс, структуру или интерфейс, распределенный по нескольким файлам. Ключевое слово partial просто помещается перед классом, структурой или интерфейсом. В следующем примере класс The. Big. Class размещается в двух отдельных файлах: Big. Class. Part 1. cs и Big. Class. Part 2. cs: // Big. Class. Partl. cs partial class The. Big. Class { public void Method. One(){ } } // Big. Class. Part 2. cs partial class The. Big. Class { public void Method. Two(){ } } Лекция 02. 10. 12 г. 12
Статические классы Статический класс функционально представляет то же самое, что класс с приватным статическим конструктором. Создать экземпляр такого класса невозможно. Если указать ключевое слово static в объявлении класса, компилятор будет гарантировать, что к этому классу никогда не будут добавлены нестатические члены. В противном случае будет выдана ошибка компиляции. Это гарантирует также, что экземпляры этого класса никогда не будут созданы. Синтаксис объявления статического класса выглядит следующим образом: static class Static. Utilities { Объект типа Static. Utilities не нужен для public static void Helper. Method(){ } } вызова Helper. Method(). Для вызова используется имя типа: Static. Utilities. Helper. Method(); Лекция 02. 10. 12 г. 13
Расширяющие методы Пусть имеется класс: Public class Money { private decimal amount; public decimal Amount { get { return amount; } set { amount = value; } } public override string To. String() { return "$" + Amount. To. String(); } } Предположим, что к этому классу необходимо добавить новый метод, но при этом нельзя изменять описание самого класса. Расширяющие методы (extension methods) — это статические методы, которые могут стать частью класса, когда нет доступа к исходному коду этого класса. Для этого создается статический класс и в него добавляется статический метод Add. To. Amount: public static class Money. Extension { public static void Add. To. Amount(this Money money, decimal amount. To. Add){ money. Amount+= amount. To. Add; } } Лекция 02. 10. 12 г. 14
Расширяющие методы Необходимо обратить внимание на параметры метода Add. To. Amount. Для расширяющего метода первым параметром всегда должен быть тип, который подлежит расширению, предваренный ключевым словом this. Это сообщит компилятору, что этот метод должен стать частью типа Money. В данном примере Money — расширяемый тип. В расширяющем методе мы получаем доступ к общедоступным методам и свойствам расширяемого типа. В главной программе метод Add. To. Amount выглядит как еще один метод класса Money. Первый параметр при этом не используется. Для доступа к новому методу выполняется его вызов, как любого другого метода: Money cash 1= new Money(); cash 1. Add. To. Amount(10); Если имя расширяющего метода совпадет с именем уже существующего метода класса, то расширяющий метод никогда не будет вызван. Любые методы экземпляра, имеющиеся в классе, имеют преимущество. Лекция 02. 10. 12 г. 15
Расширяющие методы Приведем полный код рассмотренного примера: //Ex 031. cs using System; class Program { static void Main(string[] args) { Money cash 1= new Money(); cash 1. Add. To. Amount(10); Console. Write. Line(cash 1. To. String()); cash 1. Add. To. Amount(40); Console. Write. Line(cash 1. To. String()); Console. Read. Line(); } } public class Money { private decimal amount; public decimal Amount { get { return amount; } set { amount = value; } } public override string To. String() { return "$" + Amount. To. String(); } } public static class Money. Extension { public static void Add. To. Amount(this Money money, decimal amount. To. Add){ money. Amount += amount. To. Add; } Лекция 02. 10. 12 г. 16 }
Методы класса Методы могут быть реализованы в рамках классов или структур (и прототипироваться внутри типов интерфейсов), а также снабжаться различными ключевыми словами (такими как internal, virtual, public, new и т. д. ), уточняющими их поведение. Хотя процесс определения метода в С# вполне понятен, существует еще несколько ключевых слов, которые можно использовать для управления тем, каким конкретно образом интересующему методу должны передаваться параметры (аргументы). Эти ключевые слова перечислены в таблице на следующем слайде. Лекция 02. 10. 12 г. 17
Модификаторы параметров методов Модификатор параметра Описание (отсутствует) Если параметр не сопровождается никаким модификатором, предполагается, что он должен передаваться по значению, т. е. вызываемый метод должен получать копию исходных данных out Выходные параметры должны назначаться вызываемым методом (и, следовательно, передаваться по ссылке). В случае назначения выходных параметров вызываемым методом, компилятор выдает ошибку ref Это значение первоначально назначается вызывающим кодом и при желании может переназначаться вызываемым методом (поскольку в этом случае данные тоже передаются по ссылке). В случае не предоставления параметра ref вызываемым методом, компилятор никакой ошибки генерировать не будет. params Этот модификатор позволяет передавать в виде одного логического параметра любое количество аргументов. В каждом методе может присутствовать только один модификатор params и он должен обязательно идти последним в списке параметров. Лекция 02. 10. 12 г. 18
Передача параметров в метод По умолчанию параметр передается функции по значению. Просто говоря, если аргумент не снабжается конкретным модификатором параметра, функции передается копия данных. Как именно будет выглядеть эта копия, зависит от того, к какому типу относится параметр: к типу-значению или к ссылочному типу. //Ex 032. cs using System; class Program { static void Main(string[] args) { Console. Write. Line ("***** Fun with Methods *****"); int x = 9, y = 10; Console. Write. Line("Before call: X: {0}, Y: {1}", x, y); Console. Write. Line("Answer is: {0}", Add(x, y)); Console. Write. Line("After call: X: {0}, Y: {1}", x, y); Console. Read. Line(); } public static int Add(int x, int y){ int ans = x + y; x = 10000; y = 88888; return ans; } } Лекция 02. 10. 12 г. 19
Модификатор out Методы, которым при определении (с помощью ключевого слова out) указывается принимать выходные параметры, должны при выходе обязательно присваивать им соответствующее значение (в случае не соблюдения этого условия, компилятор будет выдавать ошибку). //Ex 033. cs using System; class Program { static void Main(string[] args) { Console. Write. Line ("***** Fun with Methods *****"); int ans; Add(90, out ans); Console. Write. Line("90 + 90 = {0}", ans); Console. Read. Line(); } public static void Add(int x, int y, out int ans) { ans = x + y; } } Лекция 02. 10. 12 г. 20
Модификатор ref Параметры, сопровождаемые модификатором ref, называются ссылочными и применяются, если нужно позволить методу работать и (обычно) изменять значения различных элементов данных, объявляемых в рамках вызывающего кода (например, в процедуре сортировки или обмена). Отличие ссылочные параметры от выходных состоит в том, что выходные параметры не нужно инициализировать передачей методу, а ссылочные параметры требуют обязательной инициализации. Если не присвоить ссылочному параметру начальное значение, это равнозначно выполнению операции над не присвоенной локальной переменной. //Ex 034. cs using System; class Program { static void Main(string[] args) { Console. Write. Line ("***** Fun with Methods *****"); string s 1 = "Flip", s 2 = "Flop"; Console. Write. Line("Before: {0}, {1} ", s 1, s 2); //до Swap. Strings(ref s 1, ref s 2); Console. Write. Line("After: {0}, {1} ", s 1, s 2); //после Console. Read. Line(); } public static void Swap. Strings(ref string s 1, ref string s 2) { string temp. Str = s 1; s 1 = s 2; s 2 = temp. Str; } Лекция 02. 10. 12 г. } 21
Модификаторы доступа В таблице представлены роли и применение модификаторов доступа. Модификатор доступа Может быть применен к Назначение public типам или членам типов Общедоступные (public) элементы не имеют ограничений доступа. Общедоступный член может быть доступен как из объекта, так и из любого производного класса. Общедоступный тип может быть доступен из других внешних сборок private членам типов или вложенным типам Приватные (private) элементы могут быть доступны только в классе (или структуре), в котором они определены protected членам типов или вложенным типам Защищенные (protected) элементы не доступны напрямую через переменнуюобъект; однако они доступны в определяющем типе, а также всех производных классах internal типам или членам типов Внутренние (internal) элементы доступны только в пределах текущей сборки. Поэтому, если вы определили набор внутренних типов внутри библиотеки классов. Net, другие сборки не смогут их использовать protected internal членам типов или вложенным типам Когда ключевые слова protected и internal комбинируются в объявлении элемента, такой элемент доступен внутри определяющей его сборки, определяющего класса и всех его наследников Лекция 02. 10. 12 г. 22
Модификаторы доступа по умолчанию По умолчанию члены типов являются неявно приватными (private) и неявно внутренними (internal). Таким образом, следующее определение класса автоматически установлено internal, в то время как конструктор по умолчанию этого типа автоматически является private: // Внутренний класс с приватным конструктором по умолчанию. class Radio { Radio() { } } Если необходимо открыть Radio внешним сборкам (это удобно при построении библиотек кода. Net), следует добавить к нему модификатор public. Public class Radio { Radio() { } } Лекция 02. 10. 12 г. 23
Другие модификаторы (virtual, abstract, override, sealed, extern) Модификаторы, перечисленные в таблице на следующем слайде, могут быть применены к членам типов и характеризуются различным использованием. Из всех приведенных ниже модификаторов internal и protected internal являются новыми в языке С# и среде. Net. Модификатор internal ведет себя в основном так же, как public, но ограничивает доступ пределами текущей сборки — т. е. пределами кода, скомпилированного в одно и то же время, в одну и ту же программу. Можно использовать internal для обеспечения того, чтобы все другие разрабатываемые классы имели доступ к определенному члену, в то же время скрывая его от кода, написанного в других организациях. Модификатор protected internal комбинирует protected и internal, но комбинирует в смысле логического "ИЛИ", а не "И". Член protected internal может быть видимым любому коду в той же сборке. Он также видим для всех производных классов, даже из других сборок. Лекция 02. 10. 12 г. 24
Другие модификаторы (virtual, abstract, override, sealed, extern) Модификатор доступа C# К чему относится Назначение new Функциям-членам класса Функция скрывает унаследованную функцию с той же сигнатурой. static Ко всем членам класса virtual Только к классам и функциям-членам класса abstract Только к функциямчленам класса Виртуальная функция, определяющая сигнатуру, но не представляющая реализацию sealed К классам, методам и свойствам Для классов означает, что класс не может быть наследован. Для свойств и методов – член класса переопределяет унаследованный виртуальный член класса, но не может быть переопределен ни одним членом производных классов. Должен применяться в сочетании с override. extern Только к статическим методом [Dll. Import] Член класса реализован внешне на другом языке override Только к функциямчленам класса Член класса не связан с конкретным экземпляром класса. Член класса может быть переопределен в классах-наследниках. Член класса переопределяет унаследованный виртуальный или абстрактный член базового класса Лекция 02. 10. 12 г. 25