построение диаграмм

UML - отличное средство моделирования, но, строить диаграммы на бумаге - не всегда удобно, хотя бы по причине сложностей с редактированием, распространением и т. д. Чтобы облегчить труд проектировщика, были созданы CASE-средства - программы специального вида. CASE-средства помогут вам построить профессионально выглядящие диаграммы, даже если вы не в состоянии провести прямую линию на бумаге!

CASE-средства (от Computer Aided Software/System Engineering) - позволяют проектировать любые системы на компьютере. Необходимый элемент системного и структурно-функционального анализа, CASE-средства позволяют моделировать бизнес-процессы, базы данных, компоненты программного обеспечения, деятельность и структуру организаций. Применимы практически во всех сферах деятельности. Результат использования CASE-средств - оптимизация систем, снижение расходов, повышение эффективности, снижение вероятности ошибок.

Существует немало подобных программ. Выбор CASE-средства "по себе" - личное дело каждого

Моделирование системы предполагает идентификацию сущностей, важных с той или иной точки зрения. Эти сущности составляют словарь моделируемой системы. Например, если вы строите дом, то для вас как домовладельца будут иметь значение стены, двери, окна, встроенные шкафы и освещение. Каждая из названных сущностей отличается от других и характеризуется собственным набором свойств. У стен есть высота и ширина, они твердые и сплошные. У дверей также есть высота и ширина, они тоже сплошные, но, кроме того, снабжены особым механизмом, позволяющим им открываться в одну сторону. Окна похожи на двери, поскольку представляют собой проемы в стенах, но в остальном свойства указанных сущностей различаются. Окна обычно (хотя и не всегда) проектируют так, чтобы через них можно было смотреть, но не проходить.

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

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

В языке UML все сущности подобного рода моделируются как классы. Класс - это абстракция сущностей, являющихся частью вашего словаря. Класс представляет не индивидуальный объект, а целую их совокупность (см. главу 13). Так, умозрительно вы можете считать, что "стена" - это класс объектов с некоторыми общими свойствами, такими как высота, длина, толщина, несущая это стена или нет, и т.д. При этом конкретные стены будут рассматриваться как отдельные экземпляры класса "стена", одним из которых является, например, "стена в юго-западной части моего кабинета".

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

18. Диаграммы классов. Класс в UML.

Диаграмма описывает структуру системы, показывая её классы, их атрибуты и операторы, а также взаимосвязи этих классов.

Классом называется описание совокупности объектов с общими атрибутами, операциями, отношениями и семантикой. Атрибут – это именованное свойство класса, включающее описание множества значений, которые могут принимать экземпляры этого класса.

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

19. Диаграммы классов. Связи в UML.

· Ассоциация (линия с точками на концах)

Некая семантическая связь (жилец – дом, курс – студент, книга – библиотека)

· Зависимость (пунктир)

Показывает, что один класс ссылается на другой. Изменение в первом классе, повлияет на изменение другого класса (от целого к частному). 1 класс является параметром метода другого класса:

· Обощение (стрелка)

Не возникает при 1м взгляде. Возникает при анализе родителей и потомков

· Агрегация (линия с ромбом на конце)

От частного к общему. Если ромб закрашен – отрицание оп значению. «целая и часть» создаются и разрушаются одновременно. Если ромб пуст - отрицание по ссылке. целая и часть» создаются и разрушаются в разное время.

20. Диаграммы классов в Visual Studio.

Для начала – экскурс в суть проблемы, если так можно выразиться. UML сегодня является стандартом для описания программных систем (да и не только программных, наверное) – штукенция эта проэволюционировала с 1995 года (т.е. ей аж 15 лет) и продолжает “радовать” нас своим присутствием. UML, как способ моделирования, попытался взять себе на плечи не только банальное описание системы (а это бы было слишком примитивно, а он посягнул на нечто большее – на генерацию кода из диаграмм, а также так называемый roundtrip engineering – это когда код который вы написали руками программа магически превращала в диаграммы, оставляя при этом реализацию на месте. В принципе, все это счастье должно было работать как часы, но вот незадача – программы которые пытались это делать все капитально провалились.

В VS2008 есть поддержка диаграмм классов (расширение.cd). Диаграммы классов – это одна из основных фич UML, суть которой в иллюстрации зависимости между классами в проекте. Именно поэтому диаграмму классов можно включить только в конкретный проект. Вот небольшая иллюстрация того, что можно получить:

Как ни странно, диаграммы классов позволяют не только быстро получить иллюстрацию (что само по себе круто), но также редактировать класс прямо на диаграмме, с моментальным обновлением кода. Круто конечно, но полезность данной фичи стремится к нулю т.к. поменять public на private можно и вручную.

Конкретно под UML есть пять типов диаграмм – нас пока интересует диаграмма классов, поэтому выберем ее. Появляется тулбокс, из которого можно перетаскивать элементы. При этом не пробуйте перетащить класс из Class Viewer – не получится!

21. Типы данных в языке С++ и С#.

Подобно языку C++ и Java, C# подразделяет типы на два вида: встроенные типы, которые определены в языке, и определяемые пользователем типы, которые выбирает программист.

С# также подразделяет типы на две другие категории: размерные и ссылочные. Основное различие между ними — это способ, которым их значения сохраняются в памяти. Размерные типы сохраняют свое фактическое значение в стеке. Ссылочные типы хранят в стеке лишь адрес объекта, а сам объект сохраняется в куче. Куча — основная память программ, доступ к которой осуществляется на много медленнее чем к стеку. Если вы работаете с очень большими объектами, то сохранение их в куче имеет много преимуществ.

В языке программирования C# типы данных подразделяются на три категории:

· Значение

К первой категории относятся участки памяти, распределенные под переменные и предназначенные для хранения значений этих переменных.

С# int x = 5; C++ int a;

· Ссылка

Переменные-ссылки содержат адреса объектов, размещенных в динамической памяти. Следующий код объявляет переменную с именем y, типа object и инициализирует ее с помощью оператора new. Таким образом она получает адрес экземпляра object, размещенного в динамической памяти (object -- это базовый класс для всех типов в C#, но об этом чуть ниже).и object y = new object(); если ссылка на пустоту- то содержит nullи собирается сборщиком (В С++ сборщиков нет)

object y = new object(); int &r = a; // ссылка на a

· Указатель

И наконец -- переменные-указатели. Они похожи на указатели в языках программирования C и C++. Очень важно понимать, что и переменные-ссылки и переменные-указатели, фактически, представляют собой адрес в памяти, но на этом их сходство заканчивается. Переменные-ссылки отслеживаются сборщиком "мусора", указатели -- нет. Переменные-указатели допускают выполнение арифметических операций над ними, ссылки -- нет. int* p, где p — указатель на целое число

int* p, int *p = &a; // указатель

Также есть предопределенные типы (int char bool..)

22. Особенности типов данных в языке С#/

См. 21. +Стек — это структура данных, которая сохраняет элементы по принципу: первым пришел, последним ушел (полная противоположность очереди). Стек относится к области памяти, поддерживаемой процессором, в которой сохраняются локальные переменные. Доступ к стеку во много раз быстрее, чем к общей области памяти, поэтому использование стека для хранения данных ускоряет работу вашей программы. В С# размерные типы (например, целые числа) располагаются в стеке: для их значений зарезервирована область в стеке, и доступ к ней осуществляется по названию переменной.

Ссылочные типы (например, объекты) располагаются в куче. Куча — это оперативная память вашего компьютера. Доступ к ней осуществляется медленнее, чем к стеку. Когда объект располагается в куче, то переменная хранит лишь адрес объекта. Этот адрес хранится в стеке. По адресу программа имеет доступ к самому объекту, все данные которого сохраняются в общем куске памяти (куче).

«Сборщик мусора» уничтожает объекты, располагающиеся в стеке, каждый раз, когда соответствующая переменная выходит за область видимости. Таким образом, если вы объявляете локальную переменную в пределах функции, то объект будет помечен как объект для «сборки мусора». И он будет удален из памяти после завершения работы функции.

23. Массивы в С++ и C#.

Массив — это структура данных, содержащая несколько переменных одного типа.

C# int[] array1 = new int[5]; int[] array2 = new int[] { 1, 3, 5}; int[] array3 = { 1, 2, 3, 4, 5, 6 }; Типы массива являются ссылочными типами, производными от абстрактного базового типа Array. C++ int array[5]; int array[5] = {0, 1, 2, 3, 4}; int arr[3][5;] двумерный

В С# массивы являются объектами, производными от базового класса System.Array. Поэтому, хотя синтаксис определения массива аналогичен C++ или Java, реально вы создаете при этом экземпляр класса.NET. Это значит, что члены каждого объявленного массива унаследованы от System.Array.

Для объявления массива на С# нужно поместить пустые квадратные скобки между именем типа и переменной, например, так:

int[] numbers;

Этот синтаксис отличен от C++, в котором квадратные скобки идут после имени переменной. Поскольку массивы основаны на классах, многие из правил объявления классов применяются и к массивам. Например, при объявлении массива на самом деле вы не создаете его. Так же, как и в случае класса, вы должны создать экземпляр массива, и только после этого он будет существовать в том смысле, что для его элементов будет выделена память. Вот как объявить массив и одновременно создать его экземпляр:

// Этот код объявляет одномерный массив

// из 6 элементов и создает его экземпляр.

int[] numbers = new int[6];

Однако, объявляя массив как член класса, вам нужно разбить определение массива и создание его экземпляра на два четко обозначенных этапа, поскольку вы не можете создавать экземпляр объекта до периода выполнения:

class YourClass { int[] numbers;

void SomelnitMethodO

numbers = new int[6]; }

24. Структуры в C#.

Структура - это объединенные данные, у которых есть некоторая логическая взаимосвязь. В отличие от массивов, структуры могут содержать данные разных типов. Описание структуры:

Struct имя_структуры

{Поле1;

Поле2;…….}

Структура является типом значения. При создании структуры переменная, к которой она назначается, сохраняет фактические данные структуры. При назначении структуры новой переменной выполняется ее копирование. Поэтому новая переменная и исходная переменная содержат две отдельных копии одних данных.=>В отличие от классов структуры можно создавать без использования оператора new.

public struct CoOrds

{public int x, y;

public CoOrds(int p1, int p2) {

x = p1; y = p2; }}

CoOrds coords1 = new CoOrds();

CoOrds coords2 = new CoOrds(10, 10);

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

Оператор new для структур действует иначе, нежели для классов и других типов по ссылке, вместо выделения памяти в куче оператор new для структуры вызывает конструктор по умолчанию, который инициализирует все поля своими значениями (заменить его не представляется возможным).

Для структуры можно определить конструктор, но только с параметрами. Нельзя определить конструктор не принимающий параметров. Для структур можно определить методы Dispose() и Close(), однако деструктор Finalize() не поддерживается.

Для структур оператор = = по умолчанию не выполняет ничего.

struct PointStruct

{ public int X, Y, Z;

public PointStruct(int initX, int initY, int initZ)

{ X = initX; Y = initY; Z = initZ; } }...

PointStruct myStruct = new PointStruct(10, 20, 30);

25. Функции в C#. Передача аргументов в C#.

Процедура – это подпрограмма, которая может вызываться в коде основной программы и производить какие либо действия с переданными в нее переменными.

Функция – это такая же процедура, только ей свойственно явное возвращение результат ее работы.

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

Так же функции и процедуру позволяют выделять участки кода в логические блоки, что помогает, потом ее быстрей разбирать и понимать, чем читать однотипный код программы.

Можно передавать объекты по значению, а можно по ссылке, для этого есть ключевое слово ref:

static void Swap(ref int a, ref int b)

{ int t = a;

a = b;

b = t;}

Если нужно просто вернуть значение из функции, а не изменить существующее значение, то нужно использовать ключевое слово out:

static void SolveSquareEquation(

double a, double b, double c,

out double x1, out double x2);

Причем соответствующее слово должно ставиться не только в заголовке функции, но и при ее вызове

swap(ref aa, ref bb);

Типы видимости:

public - функции могут вызывать кто угодно

protected - функции могут вызывать только производные классы

private - функции могут вызываться только из этого же класса,

то же самое относится ко всему, что находится внутри класса

Функции и наследование

При наследовании можно скрыть функцию, для этого потребуется перед ней написать ключевое слово new. Можно переопределить, использовав ключевое слово override. Но переопределить можно только те функции, которые были объявлены ключевым словом virtual, как виртуальные (в отличие от Java, функции в C# по умолчанию невиртуальные)

26. Классы в языке С++ и в С#.

Как и в C++, классы и структуры являются основными рабочими объектами языка. Эти две конструкции языка представляют одинаковые понятия, за исключением одного свойства. Переменные структур являются типами-значениями, а классов — типами-ссылками. То есть присвоение одной структуры другой вызывает операцию копирования содержимого одной из них в другую, а после присвоения переменной типа класса другой приводит к тому, что обе они ссылаются на один объект.

Подобно классам в C++, классы в C# могут содержать методы и поля. Кроме того, классы могут содержать некоторые другие конструкции языка, такие, как события и свойства. У класса может быть только один базовый класс, вернее — всегда один, так как если класс не наследует никакие пользовательские классы, то он неявно наследуется от базового класса object. В то же время, C# допускает реализацию нескольких интерфейсов в одном классе.

Все сказанное о классах справедливо и для структур, за исключением наследования. В отличие от классов все структуры неявно наследуются от типа ValueType, а наследование от самих структур запрещено. При этом структуры, как и классы, могут реализовывать один или несколько интерфейсов.

27. Реализация инкапсуляции в языках программирования.

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

Практическая важность концепции инкапсуляции для современных языков объектно-ориентированного программирования (в том числе и для языка C#) определяется следующими фундаментальными свойствами.

Прежде всего, реализация концепции инкапсуляции обеспечивает совместное хранение данных (или, иначе, полей) и функций (или, иначе, методов) внутри объекта.

Как следствие, механизм инкапсуляции приводит к сокрытию информации о внутреннем «устройстве» объекта данных (или, в терминах языков ООП, свойств и методов объекта) от пользователя того или иного объектно-ориентированного приложения. Таким образом, пользователь, получающий программное обеспечение как сервис, оказывается изолированным от особенностей среды реализации.

Рассмотрим в достаточно обобщенном виде схему взаимодействия объекта и данных. Вначале представим схему такого рода для традиционного императивного языка программирования (каковыми являются, например, языки C и Pascal). При этом в нашем рассуждении под термином «объект» будем понимать произвольный объект языка программирования, безотносительно к концепции объектно-ориентированного программирования.

В этом случае, объявления данных и процедуры обработки данных отделены друг от друга. Зачастую в ранних языках программирования описания языковых объектов и процедуры манипулирования ими выделены в обособленные разделы.

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

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

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

По существу, объект и данные составляют единое и неделимое целое. Прежде всего, определение (или описание свойств классов) и процедуры манипулирования этими свойствами (или методы) для каждого объекта языка программирования при объектно-ориентированном подходе хранятся совместно.

Кроме того, среда проектирования и реализации программного обеспечения (например, Microsoft Visual Studio.NET) не предоставляет иных возможностей доступа к объекту, как

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

Таким образом, концепция инкапсуляции в языках объектно-ориентированного программирования служит целям обеспечения единообразия определения, хранения и манипулирования информацией о языковых объектах.

28. Организация доступа к полям в объектно-ориентированных языках программирования.

Степень инкапсуляции объекта зависит от вида области видимости:

1. public (доступность из любого места, для которого известно пространство имен с описанием объекта):

- элементы интерфейсов и перечислений являются public по умолчанию;

- типы в пространствах имен (классы, структуры, интерфейсы, перечисления, делегаты) по умолчанию являются доступными как internal (видимы из сборки с описанием объекта);

2. private (доступность из описания класса или структуры):

- элементы классов и структур (поля, методы, свойства, вложенные типы и т.д.) являются private по умолчанию.

Виды дополнительных областей видимости объектов в языке C#:

1) protected: доступность из класса с описанием объекта и его подклассов;

2) internal: доступность из сборки с описанием объекта;

3) protected internal: доступность из класса с описанием объекта, его подклассов, а также из сборки с описанием объекта.

После обсуждения использования (степеней) инкапсуляции применительно к классам в целом, рассмотрим особенности приложения данной концепции к таким фундаментальным объектам языка программирования C# как поля и константы.

Инициализация не является безусловно необходимой для полей в языке программирования C#. Тем не менее, доступ к полям и методам изначально запрещен (что соответствует использованию по умолчанию модификатора доступа private). В случае структуры поля инициализации не подлежат.

Простейшей иллюстрацией описания поля в языке программирования C# является следующий пример, содержащий определение класса C с целочисленным полем value:

class C { int value = 0; }

В случае константы инициализация также не является необходимым требованием. Тем не

менее, значение константы должно быть вычислимо в процессе компиляции.

Простейшей иллюстрацией описания константы в языке программирования C# является

следующий пример, содержащий определение константы size, представляющей собой

целочисленное значение двойной длины (long): const long size = ((long)int.MaxValue+1)/4;

Особым случаем реализации концепции инкапсуляции в языке объектно-ориентированного программирования C# является механизм полей, предназначенных

только для чтения. Для описания такого рода полей в языке C# используется зарезервированное слово readonly.

Основные свойства полей, предназначенных только для чтения, сводятся к следующему.

Прежде всего, такие поля необходимо инициализировать непосредственно при описании

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

вычислено во время выполнения программы. Наконец, поля, предназначенные только для чтения, занимают предназначенные для них области памяти (что до определенной степени аналогично статическим объектам).

Проиллюстрируем основные свойства полей, предназначенных только для чтения, следующими примерами фрагментов программ на языке C#. Описание поля выглядит следующим образом:

readonly DateTime date;

Доступ к полю изнутри класса организуется по краткому имени объекта:

... value... size... date...

Доступ к полю из других классов (на примере объекта c как конкретизации класса C)

реализуется с указанием полного имени объекта:

c = new C();

... c.value... c.size... c.date...

Рассмотрим подробнее особенности реализации механизмов доступа к статическим полям

и константам в языке программирования C#.

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

динамических по своей природе и порожденных динамическими классами, не затрагивают

статических полей и констант, последние принадлежат классам, а не объектам.

Проиллюстрируем это высказывание следующим примером фрагмента программы на языке C#:

class Rectangle {

static Color defaultColor; //однократно для класса

static readonly int scale; //однократно для класса

// статические константы недопустимо использовать

int x, y, width,height; //однократно для объекта...}

Как видно из приведенного примера, при описании класса Rectangle со статическими полями Color и scale и динамическими полями x, y, width и height, статическими являются поля, инвариантные по отношению к классу, а динамическими – представляющие собой «неподвижную точку» относительно объекта.

29. Свойства в С#.

Свойство сочетает в себе поле с методами доступа к нему. Свойство состоит из имени и аксессоров get и set. Аксессоры служат для получения и установки значения переменной. Главное преимущество свойства заключается в том, что его имя может быть использовано в выражениях и операторах присваивания аналогично имени обычной переменной, но в действительности при обращении к свойству по имени автоматически вызываются его аксессоры get и set.

Ниже приведена общая форма свойства:

тип имя {

get {

// код аксессора для чтения из поля}

set {

// код аксессора для записи в поле}

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

30. Описание объектов в С++ и в C#.

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

Ключевыми понятиями в ООП являются объекты и классы. Классы - это структуры, в которые добавили функции. А объекты - это структурные переменные.

Определение класса

Определение класса должно располагаться до main.

Начнём с простых примеров:

class soldier

{public: int x,y;

int ammo;};

В данном примере определение класса почти идентично определению структур. Есть только два отличия: в заголовке вместо ключевого слово struct стоит class. Второе - в первой строке определения класса стоит public с двоеточием. Сначала создадим переменную типа soldier.

soldier a;

a.x = 3;

a.y = 4;

a.ammo = 5;

Здесь мы создали объект a класса soldier. Смотрите, совсем никаких отличий от структурных переменных. В данном случае объекты (переменные классов) можно использовать также как и структурные переменные.

В большинстве объектно-ориентированных языков есть две отдельных категории типов: базисные (primitive types), т. е. присущие языку, и классы — типы, которые может создать пользователь языка. Как вы уже могли догадаться, к базисным относятся обычно такие простые типы, как символы, строки и числа, тогда как классы чаще представляют собой более сложные конструкции.

Существование двух категорий типов ведет к возникновению массы проблем. Одна из них связана с совместимостью. Допустим, вам потребовалась совокупность значений типа int в традиционной системе, где имеются оба набора типов. При этом нужно будет создать класс, содержащий исключительно значения типа int. А если еще потребуется класс, содержащий совокупность значений типа double, вам придется делать то же самое и для данного типа. Причина в том, что эти базисные типы обычно не имеют ничего общего. Они не являются настоящими объектами и поэтому не происходят от общего базового класса — к каждому из них применяются собственные правила. Аналогичная проблема обнаруживается при попытке в таких традиционных системах задать метод, принимающий аргумент любого типа, поддерживаемого языком. Поскольку базисные типы несовместимы, невозможно задать использование подобного аргумента, если не написать для каждого базисного типа класс-оболочку.

К счастью, в мире.NET и С# такой проблемы уже нет, поскольку в CTS любая сущность — объект. Более того, все объекты косвенно происходят от единого базового класса, определенного в составе CTS. Этот базовый класс — System.Object

31. Конструкторы и деструкторы.

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

Конструктор без параметров public worker() { }

C параметрами: public worker(string f,char p,) { fio=f; pol=p; }

Возможность обеспечивать очистку и освобождение ресурсов после завершения исполнения компонентов — одна из самых важных функций системы на основе компонентов. Под "очисткой и освобождением ресурсов" я понимаю своевременное освобождение ссылок на другие компоненты и освобождение ресурсов, количество которых невелико или ограничено, за которые идет конкуренция (например, соединения с базой данных, описатели файлов и коммуникационные порты). Под "завершением" я понимаю тот момент, когда объект более не используется. В C++ очистку осуществляет деструктор (destructor) объекта — определенная для каждого объекта C++ функция, автоматически выполняемая при выходе объекта из области видимости. В мире Microsoft.NET очистку объектов автоматически производит.NET Garbage Collector (GC). В силу некоторых причин эта стратегия противоречива, поскольку в отличие от предсказуемости C++ исполнение кода завершения объекта в решениях.NET основано на модели с отложенными вычислениями ("lazy" model). GC использует фоновые потоки, определяющие, что ссылок на объект больше не осталось. Другие потоки GC в свою очередь отвечают за исполнение кода завершения данного объекта. Чаще всего это то, что нужно, но это далеко не оптимальное решение, когда мы имеем дело с ресурсами, которые необходимо своевременно освобождать в предсказуемом порядке. И решить эту проблему ой как нелегко. В этом разделе я опишу проблемы, связанные с завершением объектов и управлением ресурсами, и расскажу, в чем заключается ваша роль в создании объектов с предсказуемым сроком жизни.

32. Перегрузка методов и операторов.

Перегрузка методов в C# означает, что в классе можно определить несколько методов с одним и тем же именем при условии, что эти методы получают разное число параметров. Пр: public void method1(),public void method1(int i, char c)

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

// Операция + с целыми.

int а = 100;

int b = 240;

int с = а + b; //с теперь равно 340

Здесь нет ничего нового, но задумывались ли вы когда-нибудь о том, что одна и та же операция + может применяться к большинству встроенных типов данных С#? Например, рассмотрим такой код:

// Операция + со строками.

string si = "Hello";

string s2 = " world!";

string s3 = si + s2; // s3 теперь содержит "Hello world!"

По сути, функциональность операции + уникальным образом базируются на представленных типах данных (строках или целых в данном случае). Когда операция + применяется к числовым типам, мы получаем арифметическую сумму операндов. Однако когда та же операция применяется к строковым типам, получается конкатенация строк.

Язык С# предоставляет возможность строить специальные классы и структуры, которые также уникально реагируют на один и тот же набор базовых лексем (вроде операции +). Имейте в виду, что абсолютно каждую встроенную операцию С# перегружать нельзя.

// Перегружаем бинарный оператор +

public static MyArr operator +(MyArr obj1, MyArr obj2)

{ MyArr arr = new MyArr();

arr.x = obj1.x + obj2.x;

arr.y = obj1.y + obj2.y;

arr.z = obj1.z + obj2.z;

return arr; }

Перегрузка методов позволяет программистам на С# многократно использовать одни и те же имена методов, меняя лишь передаваемые аргументы. Это очень полезно, по крайней мере, в двух сценариях. Первый. Вам нужно иметь единое имя метода, поведение которого немного различается в зависимости от типа переданных аргументов. Допустим, у вас есть класс, отвечающий за протоколирование и позволяющий вашему приложению записывать на диск диагностическую информацию. Чтобы немного повысить гибкость класса, вы можете создать несколько форм метода Write, определяющих тип записываемой информации. Кроме собственно строки, подлежащей записи, метод также может принимать строку идентификатора ресурса. Без перегрузки методов вам пришлось бы реализовать отдельные методы наподобие WriteString и WriteFromResourceid для каждой ситуации. Однако перегрузка методов позволяет реализовать оба метода под именем WriteEntry, при этом они будут различаться лишь типом параметра:

public void WriteEntry(string entry)

{ Console.WriteLine(entry); }

public void WriteEntry(int resourceld)

{ Console.WriteLine("получить строку no id ресурса и вывести в log") }

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

public CommaDelimitedFile(String fileName)

{ Console.NriteLine("Constructed with a file name"); }

public CommaDelimitedFile(File file)

{ Console.WriteLine("Constructed with a file object"); }

33. Наследование. Виды наследования в объектно-ориентированных языках программирования. глава 2 с 30

Иерархия - это упорядочение абстракций, расположение их по уровням.

Основными видами иерархических структур применительно к сложным системам являются структура классов (иерархия "is-a") и структура объектов (иерархия "part of").

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

Семантически, наследование описывает отношение типа "is-a". Например, медведь есть млекопитающее, дом есть недвижимость и "быстрая сортировка" есть сортирующий алгоритм. Таким образом, наследование порождает иерархию "обобщение-специализация", в которой подкласс представляет собой специализированный частный случай своего суперкласса. "Лакмусовая бумажка" наследования - обратная проверка; так, если B не есть A, то B не стоит производить от A.

34. Наследование и совместимость типов. Поведение методов при наследовании.

Наследование (субклассирование) – это способность одного класса использовать характеристики другого. Оно использует отношение «вид чего-либо» между двумя классами. Этот простой принцип означает, что если вы хотите создать новый класс, лишь немного отличающийся от известного, то нет необходимости в переписывании заново полей и методов уже существующих у класса-предка.

Предок (ancestor) – это класс, представляющий свои возможности и характеристики другим классам через механизм наследования.

Потомок(descendant) – Класс, который использует характеристики другого класса посредством наследования.

Непосредственный предок, от которого данный класс прямо происходит, называется родителем (parent).

• Пользовательский класс может иметь только одного родителя.

• Особенностью наследования является то, что:

✓ оно расширяет возможности в том смысле, что любой класс-потомок имеет доступ или наследует практически все ресурсы (методы, поля и свойства) родительского класса и всех его предков до самого верхнего уровня иерархии, т.е. до класса TObject. Наследование поддерживает повторное использование кода у потомков;

✓ наследование позволяет потомку усовершенствовать родительский класс, сделав его более специализированным. В определении класса-потомка указываются лишь новые методы, замещающие методы и новые поля.

• В классах-потомках можно переносить объявления из одного раздела в другой, с большей видимостью, кроме раздела Private, из которого в разделы с большей видимостью не переносят.

Наследование полей

• Поля, унаследованные от класса-родителя, располагаются перед новыми.

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

• В классе-потомке можно объявить одноименное поле другого типа и этим скрыть прямой доступ к переопределенному полю. Тем не менее доступ будет возможен к обоим полям:

✓ к новому – напрямую: Obj.<поле>;

✓ к переопределенному – используя приведение типов: TParentClass(Obj). <поле>;

• При переопределении поля можно перенести его объявление в раздел класса с другой видимостью, однако нельзя перенести объявления полей из раздела Private.

Поведение методов при наследовании

По тому, какие действия происходят при вызове, методы делятся на четыре группы: Static (статические), Virtual (виртуальные), Dynamic (динамические), Abstract (абстрактные). Одной из проблем наследования является диспетчеризация вызовов методов объектов, под которой понимается то, каким образом приложение будет определять какой код требуется выполнить при вызове того или иного метода. Во многом это определяется видом метода.

Статические методы

По умолчанию все методы класса являются статическими. Транслятор разрешает вызовы статических методов на фазе трансляции. К моменту запуска программы адреса вызова статических методов уже известны и, поэтому они вызываются быстрее других. Этот механизм называют еще ранним связыванием (early binding).

Помимо всего прочего, порожденный тип наследует совместимость типов всех своих порождающих типов.

Эта расширенная совместимость типов принимает три формы:

1) между реализациями объектов;

2) между указателями на реализации объектов;

3) между формальными и фактическими параметрами.

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

35. Раннее и позднее связывание. Виртуальные методы. Полиморфизм в объектно-ориентированных языках программирования.

Полиморфизм основывается на возможности включения в данные объекта информации о методах их обработки (в виде указателей на функции). Будучи доступным в некоторой точке программы, даже при отсутствии полной информации о его типе, он всегда может корректно вызвать свойственные ему методы.

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

Полиморфный метод в Си++ называется виртуальной функцией, позволяющей получать ответы на сообщения, адресованные объектам, точный вид которых неизвестен. Такая возможность является результатом позднего связывания.

При позднем связывании адреса определяются динамически во время выполнения программы, а не статически во время компиляции, как в традиционных компилируемых языках, в которых применяется раннее связывание. Сам процесс связывания заключается в замене виртуальных функций на адреса памяти.

Виртуальные функции определяются в родительском классе, а в производных классах происходит их доопределение и для них создаются новые реализации. При работе с виртуальными функциями сообщения передаются как указатели, которые указывают на объект вместо прямой передачи объекту. Виртуальные функции используют таблицу для адресной информации, которая инициализируется на этапе выполнения при помощи конструктора.

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

В примере доступ к функциям vfun() организован через указатель bp на базовый класс. Когда он принимает значение адреса объекта базового класса, то вызывается функция из базового класса. Когда указателю присваиваются значения ссылок на объекты производных классов &D1, &D2, выбор соответствующего экземпляра функции определяется именно объектом.

# inclube <stdio.h>

class base { public: virtual void vfun (int i)

{ printf("\nbase::i =",i); } };

class dir1: public base { public: void vfun (int i)

{ printf("\ndir1::i =",i); } };

class dir2: public base { public: void vfun (int i)

{ printf("\nbase::i =",i); } };

void main (void)

{ base B, *bp = &B;

dir1 D1, *dp1 = &D1;

dir2 D2, *dp2 = &D2;

base *pbd = &D;

bp->vfun (1); // Печатает: base::i = 1

dp1->vfun (2); // Печатает: dir1::i = 2

dp1->vfun (3); // Печатает: dir2::i = 3

bp =&D1; bp->vfun (4); // Печатает: dir1::i = 4

bp =&D2; bp->vfun (5); // Печатает: dir1::i = 5}

Виртуальной функцией может быть только нестатическая компонентная функция. Виртуальной не может быть глобальная функция.

Чтобы компонент класса был в единственном экземпляре и не тиражировался при создании каждого нового объекта, он должен быть определен в классе как статический. Различие между вызовом статического метода и динамического заключается в том, что в первом случае компилятору заранее известна связь объекта с методом и он устанавливает ее на этапе выполнения компиляции.

Метод становится виртуальным, если за его объявлением в типе объекта стоит зарезервированное слово virtual. Если метод объявлен в родительском типе как virtual, то все методы с аналогичными именами в дочерних типах должны быть объявлены виртуальными.

36. Индексаторы в C#.

Индексаторы позволяют получить доступ к объекту, как будто это массив. Если определить для класса индексатор, это укажет компилятору, что делать если он встретит код, в котором экземпляр класса рассматривается так, как будто это массив.

Индексаторы определяются примерно также как и свойства (с использованием get и set). Основное отличие – что в качестве имени индексатора используется ключевое слово this.

Строка public double this[int i] говорит, что мы хотим рассматривать каждый экземпляр класса (структуры) Vector как одномерный массив с int в качестве индекса и что тип возвращаемого значения double.

Пример идексатора в C#:

public double this[int i]

{ get

{ switch (i)

{ case 0: return x; // break и не надо

case 1: return y;

case 2: return z;

// вызываем исключение

default: throw new IndexOutOfRangeException("Отсуствует эл." + i); } }

set

{ switch (i)

{ case 0: x = value; break;

case 1: y = value; break;

case 2: z = value; break;

default: throw new IndexOutOfRangeException("Отсуствует эл." + i); } } }

37. Интерфейсы в C#.

Интерфейс представляет собой полностью абстрактный класс, все методы которого абстрактны. Класс, наследующий интерфейс, обязан полностью реализовать все методы интерфейса. В этом отличие от класса, наследующего абстрактный класс, где потомок может реализовать лишь некоторые методы родительского абстрактного класса, оставаясь абстрактным классом. Интерфейсы позволяют частично справиться с таким существенным недостатком языка, как отсутствие множественного наследования классов. Хотя реализация множественного наследования сталкивается с рядом проблем, его отсутствие существенно снижает выразительную мощь языка. В языке C# полного множественного наследования классов нет. Чтобы частично сгладить этот пробел, допускается множественное наследование интерфейсов. Обеспечить возможность классу иметь несколько родителей - один полноценный класс, а остальные в виде интерфейсов, - в этом и состоит основное назначение интерфейсов.

проблемы - коллизию имен( два или более интерфейса имеют методы с одинаковыми именами и сигнатурой. Если сигнатуры разные, то это не приводит к конфликтам) и наследование от общего предка( Проблема наследования от общего предка характерна, в первую очередь, для множественного наследования классов. Если класс C является наследником классов A и B, а те, в свой черед, являются наследниками класса P, то класс наследует свойства и методы своего предка P дважды: один раз получая их от класса A, другой - от B.).

1- Реализация методов интерфейса как закрытых методов/ Для реализации этой стратегии класс, наследующий интерфейс, объявляет методы без модификатора доступа, что по умолчанию соответствует модификатору private, и уточняет имя метода именем интерфейса. Их нельзя, как ранее, вызвать по имени или даже по полному имени. Нужно явно задать цель вызова. В качестве цели следует взять текущий объект this и привести его к типу интерфейса((IStrings)this)

38. Делегаты в C#.

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

Делегаты используются для передачи методов в качестве аргументов к другим методам.

Обработчики событий — это ничто иное, как методы, вызываемые с помощью делегатов.

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

Делегат — эти тип, который безопасно инкапсулирует метод. delegate возвращаемый_тип имя (список_параметров); delegate int MyDel (int I, char C)

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

Понятия делегатов и событий всецело связаны друг с другом. Делегаты это указатели на функции. Delegate это класс. Когда высоздаете экземпляр этого класса, необходимо передать имя функции параметром конструктора класса делегата. На переданную функцию и будет ссылаться делегат.

Каждый делегат имеет сигнатуру. Делегат объявляется таким образом:

Delegate int SomeDelegate(string s, bool b);

Когда я говорю что делегат имеет сигнатуру, то я имею ввиду что делегат возвращает int и имеет два параметра string и bool.

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

Рассмотрим следующую функцию:

private int SomeFunction(string str, bool bln){...}

Вы можете передать эту функцию в конструктор класса делегата SomeDelegate, потому что сигнатуры делегата и функции соответствуют.

SomeDelegate sd = new SomeDelegate(SomeFunction);

Теперь, sd ссылается на SomeFunction, или другими словами, SomeFunction зарегестрирована в sd. Если вы вызываете sd, SomeFunction будет также вызвана.

sd("somestring", true);

39. События в C#.

Событие в объектно-ориентированное программировании — это сообщение, которое возникает в различных точках исполняемого кода при выполнении определённых условий.

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

Button является классом, когда вы на неё нажимаете, срабатывает событие Click.

Timer является классом, каждую миллисекунду срабатывает событие Tick.

Хотите узнать что в этот момент происходит? Давайте по порядку:

У нас есть класс Counter. Этот класс содержит метод CountTo(int countTo, int reachableNum) который начинает отсчет от 0 до countTo, и запускает событие NumberReached когда значение счета достигает reachableNum.

Наш класс содержит событие: NumberReached. События - это переменные с типом делегата. Я имею ввиду что если вы хотите объявить событие, то вы просто объявляете событие с тем же типом что и делегат и ставите ключевое слово event перед объявлением. Например как сделано здесь:

public event NumberReachedEventHandler NumberReached;

В вышеописаном объявлении, NumberReachedEventHandler это делегат. Вы видите, перед тем как мы объявляем событие, мы должны определить делегат (обработчик события - event handler). Это должно выглядеть примерно так:

public delegate void NumberReachedEventHandler(

object sender, NumberReachedEventArgs e);

Как вы видите, имя делегата: NumberReachedEventHandler, и его сигнатура содержит возвращаемый тип void и 2 параметра object и NumberReachedEventArgs. Если вы где-либо собираетесь объявить этот делегат, то функция переданная в конструктор делегата должна иметь ту же сигнатуру

40. Исключения.

В С# исключения представляются классами. Все классы исключений порождены от встроенного класса исключений Exception, который определен в пространстве имен System. Управление обработкой исключений основывается на использовании оператора try. Синтаксис оператора:

try // контролируемый блок

{ …}

catch //один или несколько блоков обработки исключений

{ …}

finally //блок завершения

{…}

Программные инструкции, которые нужно проконтролировать на предмет исключений, помещаются в блок try. Если исключение возникает в этом блоке, оно дает знать о себе выбросом определенного рода информации. Выброшенная информация может быть перехвачена и обработана соответствующим образом с помощью блока catch. Любой код, который должен быть обязательно выполнен при выходе из блока try, помещается в блок finally.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: