- •Visual Studio .Net - открытая среда разработки
- •Открытость
- •Framework .Net - единый каркас среды разработки
- •Библиотека классов fcl - статический компонент каркаса
- •Единство каркаса
- •Встроенные примитивные типы
- •Структурные типы
- •Архитектура приложений
- •Модульность
- •Общеязыковая исполнительная среда clr - динамический компонент каркаса
- •Двухэтапная компиляция. Управляемый модуль и управляемый код
- •Виртуальная машина
- •Дизассемблер и ассемблер
- •Метаданные
- •Сборщик мусора - Garbage Collector - и управление памятью
- •Исключительные ситуации
- •События
- •Общие спецификации и совместимые модули
- •Создание c#
- •Виды проектов
- •Консольный проект
- •Windows-проект
- •Начало начал - точка "большого взрыва"
- •Выполнение проекта по умолчанию после "большого взрыва"
- •Проект WindowsHello
- •На этом мы закончим первое знакомство с проектaми на c# и в последующих лекциях приступим к сОбщий взгляд
- •Система типов
- •Типы или классы? и типы, и классы
- •Семантика присваивания
- •Преобразование к типу object
- •Примеры преобразований
- •Семантика присваивания. Преобразования между ссылочными и значимыми типами
- •Операции "упаковать" и "распаковать" (boxing и unboxing).
- •Где, как и когда выполняются преобразования типов?
- •Преобразования ссылочных типов
- •Преобразования типов в выражениях
- •Преобразования внутри арифметического типа
- •Явные преобразования
- •Преобразования строкового типа
- •Преобразования и класс Convert
- •Проверяемые преобразования
- •Исключения и охраняемые блоки. Первое знакомство
- •Опасные вычисления в охраняемых проверяемых блоках
- •Опасные вычисления в охраняемых непроверяемых блоках
- •Опасные преобразования и методы класса Convert
- •Объявление переменных
- •Проект Variables
- •Синтаксис объявления
- •Время жизни и область видимости переменных
- •Глобальные переменные уровня модуля. Существуют ли они в c#?
- •Локальные переменные
- •Глобальные переменные уровня процедуры. Существуют ли?
- •Константы
- •Выражения
- •Приоритет и порядок выполнения операций
- •Перегрузка операций
- •С чего начинается выполнение выражения
- •Операции "увеличить" и "уменьшить" (increment, decrement)
- •Операции sizeof и typeof
- •Как получить подробную информацию о классе?
- •Статические поля и методы арифметических классов
- •Операция new
- •Арифметические операции
- •Операции отношения
- •Операции проверки типов
- •Операции сдвига
- •Логические операции
- •Условное выражение
- •Операция приведения к типу
- •В данном примере явное преобразование из типа double в тип int выполняется, а преобразованиПрисваивание
- •Специальные случаи присваивания
- •Определенное присваивание
- •Еще раз о семантике присваивания
- •Рассмотрим объявления:
- •Класс Math и его функции
- •Класс Random и его функции
- •Операторы языка c#
- •Оператор присваивания
- •Блок или составной оператор
- •Пустой оператор
- •Операторы выбора
- •Оператор if
- •Оператор switch
- •Операторы перехода
- •Оператор goto
- •Операторы break и continue
- •Оператор return
- •Операторы цикла
- •Оператор for
- •Циклы While
- •Цикл foreach
- •Процедуры и функции - функциональные модули
- •Процедуры и функции - методы класса
- •Процедуры и функции. Отличия
- •Описание методов (процедур и функций). Синтаксис
- •Список формальных аргументов
- •Тело метода
- •Вызов метода. Синтаксис
- •О соответствии списков формальных и фактических аргументов
- •Вызов метода. Семантика
- •Что нужно знать о методах?
- •Почему у методов мало аргументов?
- •Поля класса или функции без аргументов?
- •Пример: две версии класса Account
- •Функции с побочным эффектом
- •Методы. Перегрузка
- •Корректность методов
- •Инварианты и варианты цикла
- •Рекурсия
- •Рекурсивное решение задачи "Ханойские башни"
- •Быстрая сортировка Хоара
- •Общий взгляд
- •Объявление массивов
- •Объявление одномерных массивов
- •Динамические массивы
- •Многомерные массивы
- •Массивы массивов
- •Процедуры и массивы
- •Класс Array
- •Массивы как коллекции
- •Сортировка и поиск. Статические методы класса Array
- •Сводка свойств и методов класса Array
- •Класс Object и массивы
- •Массивы объектов
- •Массивы. Семантика присваивания
- •Общий взгляд
- •Строки с#
- •Класс char
- •Класс char[] - массив символов
- •Существует ли в c# тип char*
- •Пространство имен RegularExpression и классы регулярных выражений
- •Немного теории
- •Синтаксис регулярных выражений
- •Знакомство с классами пространства RegularExpressions
- •Класс Regex
- •Классы Match и MatchCollection
- •Классы Group и GroupCollection
- •Классы Capture и CaptureCollection
- •Перечисление RegexOptions
- •Класс RegexCompilationInfo
- •Примеры работы с регулярными выражениями
- •Пример "чет и нечет"
- •Пример "око и рококо"
- •Пример "кок и кук"
- •Пример "обратные ссылки"
- •Пример "Дом Джека"
- •Пример "Атрибуты"
- •Классы и ооп
- •Две роли классов
- •Синтаксис класса
- •Поля класса
- •Доступ к полям
- •Методы класса
- •Доступ к методам
- •Методы-свойства
- •Индексаторы
- •Операции
- •Статические поля и методы класса
- •Константы
- •Конструкторы класса
- •Деструкторы класса
- •Проектирование класса Rational
- •Свойства класса Rational
- •Конструкторы класса Rational
- •Методы класса Rational
- •Закрытый метод нод
- •Печать рациональных чисел
- •Тестирование создания рациональных чисел
- •Операции над рациональными числами
- •Константы класса Rational
- •Развернутые и ссылочные типы
- •Классы и структуры
- •Структуры
- •Синтаксис структур
- •Класс Rational или структура Rational
- •Встроенные структуры
- •Еще раз о двух семантиках присваивания
- •Перечисления
- •Персоны и профессии
- •Отношения между классами
- •Отношения "является" и "имеет"
- •Отношение вложенности
- •Расширение определения клиента класса
- •Отношения между клиентами и поставщиками
- •Сам себе клиент
- •Наследование
- •Добавление полей потомком
- •Конструкторы родителей и потомков
- •Добавление методов и изменение методов родителя
- •Статический контроль типов и динамическое связывание
- •Три механизма, обеспечивающие полиморфизм
- •Пример работы с полиморфным семейством классов
- •Абстрактные классы
- •Классы без потомков
- •Интерфейсы
- •Две стратегии реализации интерфейса
- •Преобразование к классу интерфейса
- •Проблемы множественного наследования
- •Коллизия имен
- •Наследование от общего предка
- •Встроенные интерфейсы
- •Упорядоченность объектов и интерфейс iComparable
- •Клонирование и интерфейс iCloneable
- •Сериализация объектов
- •Класс с атрибутом сериализации
- •Интерфейс iSerializable
- •Как определяется функциональный тип и как появляются его экземпляры
- •Функции высших порядков
- •Вычисление интеграла
- •Построение программных систем методом "раскрутки". Функции обратного вызова
- •Наследование и полиморфизм - альтернатива обратному вызову
- •Делегаты как свойства
- •Операции над делегатами. Класс Delegate
- •Пример "Комбинирование делегатов"
- •Пример "Плохая служба"
- •Классы с событиями
- •Класс sender. Как объявляются события?
- •Делегаты и события
- •Как зажигаются события
- •Классы receiver. Как обрабатываются события
- •Классы с событиями, допустимые в каркасе .Net Framework
- •Пример "Списки с событиями"
- •Класс sender
- •Классы receiver
- •Две проблемы с обработчиками событий
- •Игнорирование коллег
- •Переопределение значений аргументов события
- •Классы с большим числом событий
- •Проект "Город и его службы"
- •Наследование и универсальность
- •Синтаксис универсального класса
- •Класс с универсальными методами
- •Два основных механизма объектной технологии
- •Стек. От абстрактного, универсального класса к конкретным версиям
- •Ограниченная универсальность
- •Синтаксис ограничений
- •Список с возможностью поиска элементов по ключу
- •Как справиться с арифметикой
- •Родовое порождение класса. Предложение using
- •Универсальность и специальные случаи классов
- •Универсальные структуры
- •Универсальные интерфейсы
- •Универсальные делегаты
- •Framework .Net и универсальность
- •Корректность и устойчивость программных систем
- •Жизненный цикл программной системы
- •Три закона программотехники Первый закон (закон для разработчика)
- •Второй закон (закон для пользователя)
- •Третий закон (закон чечако)
- •Отладка
- •Создание надежного кода
- •Искусство отладки
- •Отладочная печать и условная компиляция
- •Классы Debug и Trace
- •Метод Флойда и утверждения Assert
- •Классы StackTrace и BooleanSwitch
- •Отладка и инструментальная среда Visual Studio .Net
- •Обработка исключительных ситуаций
- •Выбрасывание исключений. Создание объектов Exception
- •Захват исключения
- •Параллельная работа обработчиков исключений
- •Блок finally
- •Класс Exception
- •Организация интерфейса
- •Форма и элементы управления
- •Взаимодействие форм
- •Модальные и немодальные формы
- •Передача информации между формами
- •Образцы форм
- •Главная кнопочная форма
- •Шаблон формы для работы с классом
- •Работа со списками (еще один шаблон)
- •Элемент управления класса ListBox
- •Наследование форм
- •Два наследника формы TwoLists
- •Огранизация меню в формах
- •Создание меню в режиме проектирования
- •Классы меню
- •Создание инструментальной панели с командными кнопками
- •Рисование в форме
- •Класс Graphics
- •Методы класса Graphics
- •Класс Pen
- •Класс Brush
- •Проект "Паутина Безье, кисти и краски"
- •Паутина Безье
- •Событие Paint
- •Кисти и краски
- •Абстрактный класс Figure
- •Классы семейства геометрических фигур
- •Класс Ellipse
- •Класс Circle
- •Класс LittleCircle
- •Класс Rect
- •Класс Square
- •Класс Person
- •Список с курсором. Динамические структуры данных
- •Классы элементов списка
- •Организация интерфейса
Что нужно знать о методах?
Знания формального синтаксиса и семантики недостаточно, чтобы эффективно работать с методами. Рассмотрим сейчас несколько важных вопросов, касающихся различных сторон работы с методами класса.
Почему у методов мало аргументов?
Методы класса имеют значительно меньше аргументов, чем процедуры и функции в классическом процедурном стиле программирования, когда не используется концепция классов. За счет чего происходит уменьшение числа аргументов у методов? Ведь аргументы играют важную роль: они передают методу информацию, нужную ему для работы, и возвращают информацию - результаты работы метода - программе, вызвавшей его.
Все дело в том, что методы класса - это не просто процедуры; это процедуры, обслуживающие данные. Все поля доступны любому методу по определению. Нужно четко понимать, что в момент выполнения программной системы работа идет не с классом, а с объектами - экземплярами класса. Из полей соответствующего объекта - цели вызова - извлекается информация, нужная методу в момент вызова, а работа метода чаще всего сводится к обновлению значений полей этого объекта. Поэтому очевидно, что методу не нужно через входные аргументы передавать информацию, содержащуюся в полях. Если в результате работы метода обновляется значение некоторого поля, то, опять-таки, не нужен никакой выходной аргумент.
Поля класса или функции без аргументов?
Поля хранят информацию о состоянии объектов класса. Состояние объекта динамически изменяется в ходе вычислений - обновляются значения полей. Часто возникающая дилемма при проектировании класса: что лучше - создать ли поле, хранящее информацию, или создать функцию без аргументов, вычисляющую значение этого поля всякий раз, когда это значение понадобится. Решение дилеммы - это вечный для программистов выбор между памятью и временем. Если предпочесть поле, то это приводит к дополнительным расходам памяти. Они могут быть значительными, когда создается большое число объектов - ведь свое поле должен иметь каждый объект. Если предпочесть функцию, то это потребует временных затрат на вычисление значения, и затраты могут быть значительными в сравнении с выбором текущего значения поля.
Если бы синтаксис описания метода допускал отсутствие скобок у функции (метода), в случае, когда список аргументов отсутствует, то клиент класса мог бы и не знать, обращается ли он к полю или к методу. Такой синтаксис принят, например, в языке Eiffel. Преимущество этого подхода в том, что изменение реализации никак не сказывается на клиентах класса. В языке C# это не так. Когда мы хотим получить длину строки, то пишем s.Length, точно зная, что Length - это поле, а не метод класса String. Если бы по каким-либо причинам разработчики класса String решили изменить реализацию и заменить поле Length соответствующей функцией, то ее вызов имел бы вид s.Length().
Пример: две версии класса Account
Проиллюстрируем рассмотренные выше вопросы на примере проектирования классов Account и Account1, описывающих такую абстракцию данных, как банковский счет. Определим на этих данных две основные операции - занесение денег на счет и снятие денег. В первом варианте - классе Account - будем активно использовать поля класса. Помимо двух основных полей credit и debit, хранящих приход и расход счета, введем поле balance, которое задает текущее состояние счета, и два поля, связанных с последней выполняемой операцией. Поле sum будет хранить сумму денег текущей операции, а поле result - результат выполнения операции. Полей у класса много, и как следствие, у методов класса аргументов будет немного. Вот описание нашего класса:
/// <summary>
/// Класс Account определяет банковский счет. Простейший
/// вариант с возможностью трех операций: положить деньги
/// на счет, снять со счета, узнать баланс.Вариант с полями
/// </summary>
public class Account
{
//закрытые поля класса
int debit=0, credit=0, balance =0;
int sum =0, result=0;
/// <summary>
/// Зачисление на счет с проверкой
/// </summary>
/// <param name="sum">зачисляемая сумма</param>
public void putMoney(int sum)
{
this.sum = sum;
if (sum >0)
{
credit += sum; balance = credit - debit; result =1;
}
else result = -1;
Mes();
}//putMoney
/// <summary>
/// Снятие со счета с проверкой
/// </summary>
/// <param name="sum"> снимаемая сумма</param>
public void getMoney(int sum)
{
this.sum = sum;
if(sum <= balance)
{
debit += sum; balance = credit - debit; result =2;
}
else result = -2;
Mes();
}//getMoney
/// <summary>
/// Уведомление о выполнении операции
/// </summary>
void Mes()
{
switch (result)
{
case 1:
Console.WriteLine("Операция зачисления денег
прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}",sum, balance);
break;
case 2:
Console.WriteLine("Операция снятия денег
прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance);
break;
case -1:
Console.WriteLine("Операция зачисления денег
не выполнена!");
Console.WriteLine("Сумма должна быть больше нуля!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance);
break;
case -2:
Console.WriteLine("Операция снятия денег
не выполнена!");
Console.WriteLine("Сумма должна быть
не больше баланса!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance);
break;
default:
Console.WriteLine("Неизвестная операция!");
break;
}
}
}//Account
Как можно видеть, только у методов getMoney и putMoney имеется один входной аргумент. Это тот аргумент, который нужен по сути дела, поскольку только клиент может решить, какую сумму он хочет снять или положить на счет. Других аргументов у методов класса нет - вся информация передается через поля класса. Уменьшение числа аргументов приводит к повышению эффективности работы с методами, так как исчезают затраты на передачу фактических аргументов. Но за все надо платить. В данном случае, усложняются сами операции работы со вкладом, поскольку нужно в момент выполнения операции обновлять значения многих полей класса. Закрытый метод Mes вызывается после выполнения каждой операции, сообщая о том, как прошла операция, и информируя клиента о текущем состоянии его баланса.
А теперь спроектируем аналогичный класс Account1, отличающийся только тем, что у него будет меньше полей. Вместо поля balance в классе появится соответствующая функция с этим же именем, вместо полей sum и result появятся аргументы у методов, обеспечивающие необходимую передачу информации. Вот как выглядит этот класс:
/// <summary>
/// Класс Account1 определяет банковский счет.
/// Вариант с аргументами и функциями
/// </summary>
public class Account1
{
//закрытые поля класса
int debit=0, credit=0;
/// <summary>
/// Зачисление на счет с проверкой
/// </summary>
/// <param name="sum">зачисляемая сумма</param>
public void putMoney(int sum)
{
int res =1;
if (sum >0)credit += sum;
else res = -1;
Mes(res,sum);
}//putMoney
/// <summary>
/// Снятие со счета с проверкой
/// </summary>
/// <param name="sum"> снимаемая сумма</param>
public void getMoney(int sum)
{
int res=2;
if(sum <= balance())debit += sum;
else res = -2;
balance();
Mes(res, sum);
}//getMoney
/// <summary>
/// вычисление баланса
/// </summary>
/// <returns>текущий баланс</returns>
int balance()
{
return(credit - debit);
}
/// <summary>
/// Уведомление о выполнении операции
/// </summary>
void Mes(int result, int sum)
{
switch (result)
{
case 1:
Console.WriteLine("Операция зачисления денег прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
case 2:
Console.WriteLine("Операция снятия денег
прошла успешно!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
case -1:
Console.WriteLine("Операция зачисления денег
не выполнена!");
Console.WriteLine("Сумма должна быть больше нуля!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
case -2:
Console.WriteLine("Операция снятия денег не выполнена!");
Console.WriteLine("Сумма должна быть
не больше баланса!");
Console.WriteLine("Cумма={0},
Ваш текущий баланс={1}", sum,balance());
break;
default:
Console.WriteLine("Неизвестная операция!");
break;
}
}
}//Account1
Сравнивая этот класс с классом Account, можно видеть, что число полей сократилось с пяти до двух, упростились основные методы getMoney и putMoney. Но, в качестве платы, у класса появился дополнительный метод balance(), многократно вызываемый, и у метода Mes теперь появились два аргумента. Какой класс лучше? Однозначно сказать нельзя, все зависит от контекста, от приоритетов, заданных при создании конкретной системы.
Приведу процедуру класса Testing, тестирующую работу с классами Account и Account1:
public void TestAccounts()
{
Account myAccount = new Account();
myAccount.putMoney(6000);
myAccount.getMoney(2500);
myAccount.putMoney(1000);
myAccount.getMoney(4000);
myAccount.getMoney(1000);
//Аналогичная работа с классом Account1
Console.WriteLine("Новый класс и новый счет!");
Account1 myAccount1 = new Account1();
myAccount1.putMoney(6000);
myAccount1.getMoney(2500);
myAccount1.putMoney(1000);
myAccount1.getMoney(4000);
myAccount1.getMoney(1000);
}
На рис. 9.1 показаны результаты работы этой процедуры.
Рис. 9.1. Тестирование классов Account и Account1