
- •1. Лекция: Visual Studio .Net, Framework .Net
- •Visual Studio .Net - открытая среда разработки
- •Открытость
- •Framework .Net - единый каркас среды разработки
- •Библиотека классов FCL - статический компонент каркаса
- •Единство каркаса
- •Встроенные примитивные типы
- •Структурные типы
- •Архитектура приложений
- •Модульность
- •Общеязыковая исполнительная среда CLR - динамический компонент каркаса
- •Двухэтапная компиляция. Управляемый модуль и управляемый код
- •Виртуальная машина
- •Дизассемблер и ассемблер
- •Метаданные
- •Сборщик мусора - Garbage Collector - и управление памятью
- •Исключительные ситуации
- •События
- •Общие спецификации и совместимые модули
- •2. Лекция: Язык C# и первые проекты
- •Создание C#
- •Виды проектов
- •Консольный проект
- •Windows-проект
- •Начало начал - точка "большого взрыва"
- •Выполнение проекта по умолчанию после "большого взрыва"
- •Проект WindowsHello
- •3. Лекция: Система типов языка С#
- •Общий взгляд
- •Система типов
- •Типы или классы? И типы, и классы
- •Семантика присваивания
- •Преобразование к типу object
- •Примеры преобразований
- •Семантика присваивания. Преобразования между ссылочными и значимыми типами
- •Операции "упаковать" и "распаковать" (boxing и unboxing).
- •4. Лекция: Преобразования типов
- •Где, как и когда выполняются преобразования типов?
- •Преобразования ссылочных типов
- •Преобразования типов в выражениях
- •Преобразования внутри арифметического типа
- •Явные преобразования
- •Преобразования строкового типа
- •Преобразования и класс Convert
- •Проверяемые преобразования
- •Исключения и охраняемые блоки. Первое знакомство
- •Опасные вычисления в охраняемых проверяемых блоках
- •Опасные вычисления в охраняемых непроверяемых блоках
- •Опасные преобразования и методы класса Convert
- •5. Лекция: Переменные и выражения
- •Объявление переменных
- •Проект Variables
- •Синтаксис объявления
- •Время жизни и область видимости переменных
- •Поля
- •Локальные переменные
- •Глобальные переменные уровня процедуры. Существуют ли?
- •Константы
- •6. Лекция: Выражения. Операции в выражениях
- •Выражения
- •Приоритет и порядок выполнения операций
- •Перегрузка операций
- •С чего начинается выполнение выражения
- •Операции "увеличить" и "уменьшить" (increment, decrement)
- •Операции sizeof и typeof
- •Как получить подробную информацию о классе?
- •Статические поля и методы арифметических классов
- •Операция new
- •Арифметические операции
- •Операции отношения
- •Операции проверки типов
- •Операции сдвига
- •Логические операции
- •Условное выражение
- •Операция приведения к типу
- •7. Лекция: Присваивание и встроенные функции
- •Присваивание
- •Специальные случаи присваивания
- •Определенное присваивание
- •Еще раз о семантике присваивания
- •Рассмотрим объявления:
- •Класс Math и его функции
- •Класс Random и его функции
- •8. Лекция: Операторы языка C#
- •Операторы языка C#
- •Оператор присваивания
- •Блок или составной оператор
- •Пустой оператор
- •Операторы выбора
- •Оператор if
- •Оператор switch
- •Операторы перехода
- •Оператор goto
- •Операторы break и continue
- •Оператор return
- •Операторы цикла
- •Оператор for
- •Циклы While
- •Цикл foreach
- •9. Лекция: Процедуры и функции - методы класса
- •Процедуры и функции - функциональные модули
- •Процедуры и функции - методы класса
- •Процедуры и функции. Отличия
- •Описание методов (процедур и функций). Синтаксис
- •Список формальных аргументов
- •Тело метода
- •Вызов метода. Синтаксис
- •О соответствии списков формальных и фактических аргументов
- •Вызов метода. Семантика
- •Что нужно знать о методах?
- •Почему у методов мало аргументов?
- •Поля класса или функции без аргументов?
- •Пример: две версии класса Account
- •Функции с побочным эффектом
- •Методы. Перегрузка
- •10. Лекция: Корректность методов. Рекурсия
- •Корректность методов
- •Инварианты и варианты цикла
- •Рекурсия
- •Рекурсивное решение задачи "Ханойские башни"
- •Быстрая сортировка Хоара
- •11. Лекция: Массивы языка C#
- •Общий взгляд
- •Объявление массивов
- •Объявление одномерных массивов
- •Динамические массивы
- •Многомерные массивы
- •Массивы массивов
- •Процедуры и массивы
- •12. Лекция: Класс Array и новые возможности массивов
- •Класс Array
- •Массивы как коллекции
- •Сортировка и поиск. Статические методы класса Array
- •Класс Object и массивы
- •Массивы объектов
- •Массивы. Семантика присваивания
- •13. Лекция: Символы и строки постоянной длины в C#
- •Общий взгляд
- •Строки С++
- •Строки С#
- •Класс char
- •Класс char[] - массив символов
- •Существует ли в C# тип char*
- •14. Лекция: Строки C#. Классы String и StringBuilder
- •Класс String
- •Объявление строк. Конструкторы класса string
- •Операции над строками
- •Строковые константы
- •Неизменяемый класс string
- •Статические свойства и методы класса String
- •Метод Format
- •Методы Join и Split
- •Динамические методы класса String
- •Класс StringBuilder - построитель строк
- •Объявление строк. Конструкторы класса StringBuilder
- •Операции над строками
- •Основные методы
- •Емкость буфера
- •15. Лекция: Регулярные выражения
- •Пространство имен RegularExpression и классы регулярных выражений
- •Немного теории
- •Синтаксис регулярных выражений
- •Классы Group и GroupCollection
- •Классы Capture и CaptureCollection
- •Перечисление RegexOptions
- •Класс RegexCompilationInfo
- •Примеры работы с регулярными выражениями
- •Пример "чет и нечет"
- •Пример "око и рококо"
- •Пример "кок и кук"
- •Пример "обратные ссылки"
- •Пример "Дом Джека"
- •Пример "Атрибуты"
- •16. Лекция: Классы
- •Классы и ООП
- •Две роли классов
- •Синтаксис класса
- •Поля класса
- •Доступ к полям
- •Методы класса
- •Доступ к методам
- •Методы-свойства
- •Индексаторы
- •Операции
- •Статические поля и методы класса
- •Константы
- •Конструкторы класса
- •Деструкторы класса
- •Проектирование класса Rational
- •Свойства класса Rational
- •Конструкторы класса Rational
- •Методы класса Rational
- •Закрытый метод НОД
- •Печать рациональных чисел
- •Тестирование создания рациональных чисел
- •Операции над рациональными числами
- •Константы класса Rational
- •17. Лекция: Структуры и перечисления
- •Развернутые и ссылочные типы
- •Классы и структуры
- •Структуры
- •Синтаксис структур
- •Класс Rational или структура Rational
- •Встроенные структуры
- •Еще раз о двух семантиках присваивания
- •Перечисления
- •Персоны и профессии
- •18. Лекция: Отношения между классами. Клиенты и наследники
- •Отношения между классами
- •Отношения "является" и "имеет"
- •Отношение вложенности
- •Расширение определения клиента класса
- •Отношения между клиентами и поставщиками
- •Сам себе клиент
- •Наследование
- •Добавление полей потомком
- •Конструкторы родителей и потомков
- •Добавление методов и изменение методов родителя
- •Статический контроль типов и динамическое связывание
- •Три механизма, обеспечивающие полиморфизм
- •Пример работы с полиморфным семейством классов
- •Абстрактные классы
- •Классы без потомков
- •19. Лекция: Интерфейсы. Множественное наследование
- •Интерфейсы
- •Две стратегии реализации интерфейса
- •Преобразование к классу интерфейса
- •Проблемы множественного наследования
- •Коллизия имен
- •Наследование от общего предка
- •Встроенные интерфейсы
- •Упорядоченность объектов и интерфейс IComparable
- •Клонирование и интерфейс ICloneable
- •Сериализация объектов
- •Класс с атрибутом сериализации
- •Интерфейс ISerializable
- •20. Лекция: Функциональный тип в C#. Делегаты
- •Как определяется функциональный тип и как появляются его экземпляры
- •Функции высших порядков
- •Вычисление интеграла
- •Построение программных систем методом "раскрутки". Функции обратного вызова
- •Наследование и полиморфизм - альтернатива обратному вызову
- •Делегаты как свойства
- •Операции над делегатами. Класс Delegate
- •Операции "+" и "-"
- •Пример "Комбинирование делегатов"
- •Пример "Плохая служба"
- •21. Лекция: События
- •Классы с событиями
- •Класс sender. Как объявляются события?
- •Делегаты и события
- •Как зажигаются события
- •Классы receiver. Как обрабатываются события
- •Классы с событиями, допустимые в каркасе .Net Framework
- •Пример "Списки с событиями"
- •Класс sender
- •Классы receiver
- •Две проблемы с обработчиками событий
- •Игнорирование коллег
- •Переопределение значений аргументов события
- •Классы с большим числом событий
- •Проект "Город и его службы"
- •22. Лекция: Универсальность. Классы с родовыми параметрами
- •Наследование и универсальность
- •Синтаксис универсального класса
- •Класс с универсальными методами
- •Два основных механизма объектной технологии
- •Стек. От абстрактного, универсального класса к конкретным версиям
- •Ограниченная универсальность
- •Синтаксис ограничений
- •Список с возможностью поиска элементов по ключу
- •Как справиться с арифметикой
- •Родовое порождение класса. Предложение using
- •Универсальность и специальные случаи классов
- •Универсальные структуры
- •Универсальные интерфейсы
- •Универсальные делегаты
- •Framework .Net и универсальность
- •23. Лекция: Отладка и обработка исключительных ситуаций
- •Корректность и устойчивость программных систем
- •Жизненный цикл программной системы
- •Три закона программотехники
- •Первый закон (закон для разработчика)
- •Второй закон (закон для пользователя)
- •Третий закон (закон чечако)
- •Отладка
- •Создание надежного кода
- •Искусство отладки
- •Отладочная печать и условная компиляция
- •Классы Debug и Trace
- •Метод Флойда и утверждения Assert
- •Классы StackTrace и BooleanSwitch
- •Отладка и инструментальная среда Visual Studio .Net
- •Обработка исключительных ситуаций
- •Обработка исключений в языках C/C++
- •Схема обработки исключений в C#
- •Выбрасывание исключений. Создание объектов Exception
- •Захват исключения
- •Параллельная работа обработчиков исключений
- •Блок finally
- •Схема Бертрана обработки исключительных ситуаций
- •Класс Exception
- •Организация интерфейса
- •Форма и элементы управления
- •Взаимодействие форм
- •Модальные и немодальные формы
- •Передача информации между формами
- •Образцы форм
- •Главная кнопочная форма
- •Шаблон формы для работы с классом
- •Работа со списками (еще один шаблон)
- •Элемент управления класса ListBox
- •Наследование форм
- •Два наследника формы TwoLists
- •Огранизация меню в формах
- •Создание меню в режиме проектирования
- •Классы меню
- •Создание инструментальной панели с командными кнопками
- •Рисование в форме
- •Класс Graphics
- •Методы класса Graphics
- •Класс Pen
- •Класс Brush
- •Проект "Паутина Безье, кисти и краски"
- •Паутина Безье
- •Событие Paint
- •Кисти и краски
- •Абстрактный класс Figure
- •Классы семейства геометрических фигур
- •Класс Ellipse
- •Класс Circle
- •Класс LittleCircle
- •Класс Rect
- •Класс Square
- •Класс Person
- •Список с курсором. Динамические структуры данных
- •Классы элементов списка
- •Организация интерфейса
6. Лекция: Выражения. Операции в выражениях
Выражения
Выражения строятся из операндов - констант, переменных, функций, - объединенных знаками операций и скобками. При вычислении выражения определяется его значение и тип. Эти характеристики однозначно задаются значениями и типами операндов, входящих в выражение, и правилами вычисления выражения. Правила также задают:
•приоритет операций;
•для операций одного приоритета порядок применения - слева направо или справа налево;
•преобразование типов операндов и выбор реализации для перегруженных операций;
•тип и значение результата выполнения операции над заданными значениями операндов определенного типа.
Программист, записывающий выражение, должен знать, по каким правилам оно будет вычисляться. Сложность в том, что эти правила, начиная с приоритета операций, варьируются от языка к языку. Давайте посмотрим, как это делается в C#.
Приоритет и порядок выполнения операций
Большинство операций в языке C#, их приоритет и порядок наследованы из языка C++. Однако имеются и различия: например, нет операции " , ", позволяющей вычислять список выражений; добавлены уже упоминавшиеся операции checked и unchecked, применимые к выражениям.
Как это обычно делается, приведем таблицу приоритетов операций, в каждой строке которой собраны операции одного приоритета, а строки следуют в порядке приоритетов, от высшего к низшему.
Приоритет |
Категория |
|
Операции |
|
Порядок |
|
|
|
|
|
|
0 |
Первичные |
|
(expr) x.y f(x) a[x] x++ x-- |
Слева |
|
|
|
|
new sizeof(t) |
|
направо |
|
|
|
typeof(t) |
checked(expr) |
|
|
|
|
unchecked(expr) |
|
|
|
|
|
|
|
|
1 |
Унарные |
|
+ - ! ~ ++x --x (T)x |
Слева |
|
|
|
|
|
|
направо |
|
|
|
|
|
|
2 |
Мультипликативные |
- * / % |
|
Слева |
|
|
(Умножение) |
|
|
|
направо |
|
|
|
|
|
|
3 |
Аддитивные (Сложение) |
+ - |
|
Слева |
|
|
|
|
|
|
направо |
|
|
|
|
|
|
4 |
Сдвиг |
|
<< >> |
|
Слева |
|
|
|
|
|
направо |
|
|
|
|
|
|
5 |
Отношения, проверка типов |
< > <= >= is as |
|
Слева |
|
|
|
|
|
|
направо |
|
|
|
|
|
|
6 |
Эквивалентность |
== != |
|
Слева |
|
|
|
|
|
|
направо |
|
|
|
|
|
|
7 |
Логическое |
|
& |
|
Слева |
|
|
|
|
|
направо |
|
|
|
|
|
|
8 |
Логическое |
исключающее |
^ |
|
Слева |
|
ИЛИ (XOR) |
|
|
|
направо |
|
|
|
|
|
|
9 |
Логическое ИЛИ (OR) |
| |
|
Слева |
|
|
|
|
направо |
|
|
|
|
|
10 |
Условное И |
&& |
|
Слева |
|
|
|
|
направо |
|
|
|
|
|
11 |
Условное ИЛИ |
|| |
|
Слева |
|
|
|
|
направо |
|
|
|
|
|
12 |
Условное выражение |
? : |
|
Справа |
|
|
|
|
налево |
|
|
|
|
|
13 |
Присваивание |
= *= /= %= |
+= -= <<= >>= &= |
Справа |
|
|
^= |= |
|
налево |
|
|
|
|
|
Перегрузка операций
Под перегрузкой операции понимается существование нескольких реализаций одной и той же операции. Большинство операций языка C# перегружены - одна и та же операция может применяться к операндам различных типов. Поэтому перед выполнением операции идет поиск реализации, подходящей для данных типов операндов. Замечу, что операции, как правило, выполняются над операндами одного типа. Если же операнды разных типов, то предварительно происходит неявное преобразование типа операнда. Оба операнда могут быть одного типа, но преобразование типов может все равно происходить - по той причине, что для заданных типов нет соответствующей перегруженной операции. Такая ситуация достаточно часто возникает на практике, поскольку, например, операция сложения не определена для младших подтипов арифметического типа. Приведу начальный фрагмент процедуры Express, предназначенной для анализа выражений:
///<summary>
///Анализ выражений
///</summary> public void Express()
{
//перегрузка операций byte b1 =1, b2 =2, b3; short sh1;
int in1;
//b3 = b1 + b2; |
//ошибка: результат типа int |
b3 = (byte)(b1+b2); |
//ошибка: результат типа int |
//sh1 = b1 + b2; |
sh1 = (short)(b1+b2); in1 = b1+ b2 + sh1;
Console.WriteLine("b3= " + b3 + " sh1= "+ sh1 +" in1= " + in1); }//Express
Разберем этот фрагмент. Начнем с первого закомментированного оператора присваивания b3 = b1+b2;. Выражение здесь простейшее, включает одну бинарную операцию сложения. Оба операнда имеют тип byte, казалось бы, и результат должен быть типа byte и без помех присвоен переменной b3. Однако это не так. Для данных типа byte нет перегруженной реализации сложения. Ближайшей операцией является сложение целых типа int. Поэтому оба операнда преобразуются к типу int, выполняется операция сложения, результат имеет тип int и не может быть неявно преобразован в тип byte, - возникает ошибка еще на этапе компиляции. Корректная запись показана в следующем операторе. Аналогичная ситуация возникает, когда в левой части оператора стоит переменная типа short, - и здесь необходимо явное приведение к типу. Этого приведения не требуется, когда в левой части стоит переменная типа int.
Давайте разберем, как в данном примере организован вывод в методе WriteLine. До сих пор при вызове метода задавалось несколько параметров и использовалась форма вывода данных с подстановкой значений параметров в строку, заданную первым параметром. Здесь же есть только один параметр - это строка, заданная сложным выражением. Операция, многократно применяемая в этом выражении, это сложение " + ". Операнды сложения имеют разный тип: левый операнд имеет тип string, правый - арифметический (byte, short, int). В этом случае арифметический тип преобразуется к типу string и выполняется сложение строк (конкатенация). Напомню, при преобразовании арифметического типа к типу string вызывается метод ToString(), определенный для всех встроенных типов. Результатом этого выражения является строка, она и будет результатом вывода метода WriteLine.
Полагаю, что разбор данного примера и материалы предыдущей лекции, где приводилась иерархия преобразований внутри арифметического типа и обсуждались вопросы выбора реализации перегруженного метода, дают необходимое представление о том, как работает перегрузка операций при вычислении выражений. В деталях, как всегда, может помочь справочная система.
С чего начинается выполнение выражения
Вычисление выражения начинается с выполнения операций высшего приоритета. Первым делом вычисляются выражения в круглых скобках - (expr), определяются значения полей объекта - x.y, вычисляются функции - f(x), переменные с индексами - a[i]. Выполнение этих операций достаточно понятно и не нуждается в комментировании. Операции checked и unchecked включают и выключают проверку преобразований арифметического типа в выражениях, которым они предшествуют. О других операциях этой категории скажу чуть подробнее.
Операции "увеличить" и "уменьшить" (increment, decrement)
Операции "увеличить на единицу" и "уменьшить на единицу" могут быть префиксными и постфиксными. К высшему приоритету относятся постфиксные операции x++ и x--. Префиксные операции имеют на единицу меньший приоритет. Главной особенностью как префиксных, так и постфиксных операций является побочный эффект, в результате которого значение x увеличивается (++) или уменьшается (--) на единицу. Для префиксных (++x, --x) операций результатом их выполнения является измененное значение x, постфиксные операции возвращают в качестве результата значение x до изменения. Приведу пример применения этих операций, дополнив метод Express новым фрагментом:
//операции increment и decrement
//Следующее выражение допустимо, но писать подобное никогда не следует in1 = ++in1 +in1+ in1++;
//in2 = ++in1 + in1++ + in1; Console.WriteLine(" in1= " + in1);
Обратите внимание, что хотя у постфиксной операции высший приоритет, это вовсе не означает, что при вычислении выражения вначале выполнится операция in1++, затем ++in1, и только потом будет проводиться сложение. Нет, вычисления проводятся в том порядке, в котором они написаны. Поскольку на входе значение in1 было равно 6, то выражение будет вычисляться следующим образом:
7(7) + 7 + 7(8),
где в скобках записан побочный эффект операции. Так что консольный вывод даст следующий результат:

in1 = 21
Операциями "увеличить" и "уменьшить" не следует злоупотреблять. Уже оператор, приведенный в нашем фрагменте, сложен для понимания из-за побочного эффекта. Понимаете ли вы, что при изменении порядка записи слагаемых, как это сделано в закомментированном операторе, результат вычисления выражения будет уже не 21, а 22?
Разный приоритет префиксных и постфиксных операций носит условный характер. Эти операции применимы только к переменным, свойствам и индексаторам класса, то есть к выражениям, которым отведена область памяти. В языках C++ и C# такие выражения называются l-value, поскольку они могут встречаться в левых частях оператора присваивания. Как следствие, запись в C# выражения < --x++ > приведет к ошибке. Едва лишь к x слева или справа приписана одна из операций, выражение перестает принадлежать к классу l-value выражений, и вторую операцию приписать уже нельзя.
Операции sizeof и typeof
Операция sizeof возвращает размер значимых типов, заданный в байтах. Пояснения требуют некоторые особенности ее применения. Она должна выполняться только в небезопасных блоках. Поэтому проект, в котором она может использоваться, должен быть скомпилирован с включенным свойством /unsafe. На рис. 6.1 показано, как на странице свойств проекта можно включить это свойство:
Далее необходимо создать небезопасный блок, например, метод класса, помеченный как unsafe, в котором уже можно вызывать эту функцию (операцию). Приведу пример такого метода, созданного в классе Testing:
Рис. 6.1. Включение свойства /unsafe
///<summary>
///определение размеров и типов
///</summary>
unsafe public static void SizeMethod()
{
Console.WriteLine("Размер типа Boolean = " + sizeof(bool)); Console.WriteLine("Размер типа double = " + sizeof(double)); Console.WriteLine("Размер типа char = " + sizeof(System.Char));