Учебное пособие (1077022), страница 9
Текст из файла (страница 9)
Базовый класс иинтерфейсы перечисляются через запятую. В Java для наследования отклассов используется ключевое слово extends, а для наследования от67интерфейсов ключевое слово implements. Аналогичный код на Javaвыгляделбыследующимобразом«classExtendedClass2extendsExtendedClass1 implements I1, I2».Обратите внимание, что для объявления методов, унаследованных отинтерфейса,неиспользуетсяключевоесловоoverride.Методы,унаследованные от интерфейса, не виртуальные, они как бы собственныеметоды класса.Как и в случае наследования от абстрактных классов Visual Studioпозволяетавтоматическигенерироватьзаглушкидляметодовинтерфейсов.
Создадим класс ClassForI1, наследуемый от интерфейса I1. Вэтом случае при нажатии правой кнопки мыши на имени интерфейса вконтекстномменюпоявляетсяпунктавтоматическойреализацииинтерфейса (рис 11).Рис. 11. Реализация интерфейса.Отличие от абстрактного класса состоит в том, что в случаенаследования от интерфейса подменю содержит два пункта «реализоватьинтерфейс» и «реализовать интерфейс явно». В случае выбора обоих68пунктов будет сгенерирован следующий код, комментарий перед методомсоответствует пункту меню:class ClassForI1 : I1{// реализовать интерфейсpublic string I1_method(){throw new NotImplementedException();}// реализовать интерфейс явноstring I1.I1_method(){throw new NotImplementedException();}}Возникает вопрос, чем реализация интерфейса отличается от явнойреализации, когда вызывается каждый из методов?Можно сформулировать данный вопрос в виде практическогопримера.
Дана следующая реализация класса:class ClassForI1 : I1{// реализовать интерфейсpublic string I1_method(){return "1";}// явно реализовать интерфейсstring I1.I1_method(){return "2";}}Каким образом, используя данную реализацию, можно вывести вконсоль число «12»?Ответ на данный вопрос приведен в виде следующего фрагмента кода:ClassForI1 c1 = new ClassForI1();string str1 = c1.I1_method();I1 i1 = (I1)c1;string str2 = i1.I1_method();Console.WriteLine(str1 + str2);69Рассмотрим данный код более подробно:Сначала создается объект класса ClassForI1:ClassForI1 c1 = new ClassForI1();Черезобъектклассаc1вызываетсяметодI1_method,соответствующий пункту «реализовать интерфейс». Метод возвращает«1»:string str1 = c1.I1_method();Производится приведение переменной класса c1 к интерфейсномутипу I1, для того чтобы вызвать метод явной реализации интерфейса:I1 i1 = (I1)c1;Затем через объект интерфейсного типа i1 вызывается методI1.I1_method, соответствующий пункту «реализовать интерфейс явно».Метод возвращает «2»:string str2 = i1.I1_method();Таким образом, чтобы вызвать метод, соответствующий пункту«реализовать интерфейс явно», необходимо привести объект класса кинтерфейсному типу.4.6 Методы расширенияВ языке C# существует уникальный механизм, который позволяетдобавлять новые методы к уже реализованным классам, в том числеклассам стандартной библиотеки.
Сами разработчики .NET активно егоиспользуют, многие методы стандартных библиотек реализованы какметоды расширения.Причем методы стандартной библиотеки могут находиться в однойсборке (файле .dll), а методы расширения – в другой сборке. Важно чтобыони принадлежали к одному пространству имен.Предположим, что нужно расширить класс ExtendedClass2 новымметодом, однако по каким-либо причинам невозможно внести изменения висходный код класса.70Тогда можно создать метод расширения следующим образом:static class ExtendedClass2Extension{public static int ExtendedClass2NewMethod(this ExtendedClass2 ec2, int i){return i + 1;}}Данный класс является обычным классом, однако у него естьнекоторые особенности. Метод расширения должен быть объявлен встатическом классе и должен быть статическим методом. Первый параметрметода расширения – объект расширяемого класса, перед которымуказывается ключевое слово this.Если откомпилировать класс ExtendedClass2Extension, то IntelliSenseдля класса ExtendedClass2 будет работать так, как показано на рис.
12.Рис. 12. Работа IntelliSense для метода расширения.В этом случае метод расширения обозначен как обычный метод, но сдополнительной стрелкой, указывающей на то, что это метод расширения.Таким образом, разработчику «кажется», что у класса ExtendedClass2появился новый метод ExtendedClass2NewMethod, однако, данный методобъявлен в отдельном классе и возможно даже в отдельной сборке. Дляуспешной реализации метода расширения, пространства имен у базовогокласса и класса, содержащего метод расширения, должны совпадать.714.7 Частичные классыЕще один уникальный механизм, существующий в языке C#, этомеханизм частичных классов. Этот механизм позволяет объявить класс внескольких файлах.В языке C# этот механизм используется вместе со средствамиавтоматической генерации кода.
В Visual Studio существует много средств,которые автоматически генерируют код для обращения к базе данных(технология Entity Framework), для сетевого взаимодействия (технологияWCF) и другие.Если класс автоматически генерируется с помощью какого-либосредства, то он по умолчанию создается как частичный с помощьюключевого слова partial. Это позволяет создать другую «часть» данногокласса в отдельном файле и вручную дописать необходимые методы кавтоматически сгенерированному классу.Казалось бы что эта возможность есть в языке С++, ведь в немпредусмотрено разделение класса на заголовочный файл и реализацию.
Нов языке С++ невозможно разделить заголовочный файл на несколькофайлов. В Java это принципиально невозможно, поскольку действуетправило один класс – один файл. Поэтому когда в Java возникланеобходимость создания заглушек для сетевого взаимодействия, то дляэтого создали специальный шаблон проектирования из несколькихклассов.Пример объявления частичного класса. Файл «PartialClass1.cs»:usingusingusingusingSystem;System.Collections.Generic;System.Linq;System.Text;namespace Classes{partial class PartialClass{int i1;72public PartialClass(int pi1, int pi2) { i1 = pi1; i2 = pi2;}public int MethodPart1(int i1, int i2){return i1 + i2;}}}Данная часть класса содержит закрытую переменную класса, методMethodPart1 и конструктор.Пример объявления частичного класса.
Файл «PartialClass2.cs»:usingusingusingusingSystem;System.Collections.Generic;System.Linq;System.Text;namespace Classes{partial class PartialClass{int i2;public override string ToString(){return "Частичный класс. i1=" + i1.ToString()+ " i2=" + i2.ToString();}public string MethodPart2(string i1, string i2){return i1 + i2;}}}Данная часть класса содержит закрытую переменную класса, методMethodPart2 и переопределение виртуального метода ToString.Работа механизма IntelliSense для частичного класса показана на рис13.Компилятор успешно соединил части частичного класса в несколькихфайлах. Методы MethodPart1 и MethodPart2, объявленные в разныхфайлах, показаны в едином откомпилированном классе.73Рис. 13. Работа IntelliSense для частичного класса.4.8 Создание диаграммы классов в Visual StudioДля удобства разработки в Visual Studio существует возможностьсоздания диаграммы классов.Для добавления диаграммы классов необходимо добавить в проектэлемент «диаграмма классов», как показано на рис.
14, 15.Нужно нажать правую кнопку мыши на проекте в дереве проектов ивыбрать в контекстном меню пункты «Добавить/Создать элемент».Затем в диалоге добавления нового элемента следует выбрать пунктменю «диаграмма классов» (файл с расширением .cd).Схема классов открывается в виде белого «холста», на который нужнопереносить файлы из обозревателя решений.
При этом все классы данногофайла автоматически попадают на диаграмму. Пример такой диаграммыприведен на рис. 16.Нотация диаграмм классов в Visual Studio практически полностьюсоответствует нотации диаграмм классов UML (Unified ModellingLanguage). Наследование классов показано незаштрихованной треугольнойстрелкой, реализация интерфейсов – стрелкой в виде незаштрихованнойокружности.При нажатии на холсте правой кнопки мыши предоставляетсявозможность выбрать в контекстном меню пункт «экспорт схемы какизображения». В этом случае вся диаграмма классов экспортируется как74изображение в графическом формате (jpeg, png, другие форматы), чтоудобно для оформления документации.Рис.
14. Добавление диаграммы классов (шаг 1).Рис. 15. Добавление диаграммы классов (шаг 2).В панели кнопок диаграммы классов предусмотрены различныеварианты отображения информации о полях классов (рис. 17): только имена полей;75 имена и типы полей; полная сигнатура.Рис. 16. Фрагмент диаграммы классов.Рис. 17.
Кнопки отображения информации о полях классов.764.9 Пример классов для работы с геометрическимифигурамиВ качестве обобщающего примера в данном разделе приведеныфрагментыпримера4–программы,реализующейработусгеометрическими фигурами.4.9.1 Абстрактный класс «Геометрическая фигура»Основа системы классов для работы с геометрическими фигурами –абстрактный класс «Геометрическая фигура»:usingusingusingusingSystem;System.Collections.Generic;System.Linq;System.Text;namespace Figures{/// <summary>/// Класс фигура/// </summary>abstract class Figure{/// <summary>/// Тип фигуры/// </summary>public string Type{get{return this._Type;}protected set{this._Type = value;}}string _Type;/// <summary>/// Вычисление площади/// </summary>public abstract double Area();/// <summary>/// Приведение к строке, переопределение метода Object77/// </summary>public override string ToString(){return this.Type + " площадью " +this.Area().ToString();}}}Класс объявлен как абстрактный с помощью ключевого слова abstract.Далее объявляется строковое свойство (property) Type, содержащеестроковое наименование фигуры.
Данной свойство объявлено в полнойформе, хотя оно содержит стандартный код и могло бы быть объявлено какавтоопределяемое свойство:public string Type { get; protected set; }Set-аксессор свойства объявлен с областью видимости protected, тоесть присваивать значение данному свойству можно только в текущемклассе и в классах-наследниках.Далее объявляется абстрактный метод вычисления площади «doubleArea()», который должен быть определен в классах-наследниках. Онвозвращает значение площади (тип double) и не принимает параметров, таккак площадь должна вычисляться на основе внутренних данных классовгеометрических фигур.Затем переопределяется виртуальный метод ToString из класса Objectдля приведения к строковому типу.4.9.2 Интерфейс IPrintИнтерфейсIPrintгеометрической фигуре:namespace Figures{interface IPrint{void Print();}}предназначендлявыводаинформациио78Интерфейс содержит метод Print(), который не принимает параметрови возвращает void.В дальнейшем классы Прямоугольник, Квадрат и Круг будутреализовывать интерфейс IPrint.
Переопределяемый метод Print() будетвыводить в консоль информацию, возвращаемую переопределеннымметодом ToString().4.9.3 Класс «Прямоугольник»КлассПрямоугольникнаследуетсяотабстрактногоГеометрическая фигура и реализует интерфейс IPrint:usingusingusingusingSystem;System.Collections.Generic;System.Linq;System.Text;namespace Figures{class Rectangle : Figure, IPrint{/// <summary>/// Высота/// </summary>double height;/// <summary>/// Ширина/// </summary>double width;/// <summary>/// Основной конструктор/// </summary>/// <param name="ph">Высота</param>/// <param name="pw">Ширина</param>public Rectangle(double ph, double pw){this.height = ph;this.width = pw;this.Type = "Прямоугольник";}/// <summary>/// Вычисление площадикласса79/// </summary>public override double Area(){double Result = this.width * this.height;return Result;}public void Print(){Console.WriteLine(this.ToString());}}}Класс содержит поля данных для высоты и ширины.














