
- •Глава 10, описывающая классы как контейнеры их статических членов,
- •Глава 13 посвящена отношениям между классами (и их объектами). Особое
- •Глава 18 включена в книгу при подготовке второго издания. Она посвящен
- •Глава 1. Объектная ориентация программ
- •1.1. Типы, классы, объекты
- •1.2. Программа на c#
- •1.3. Пространство имен
- •1.4. Создание консольного приложения
- •Глава 2. Типы в языке c#
- •2.1. Типы ссылок и типы значений
- •2.2. Классификация типов c#
- •2.3. Простые типы. Константы-литерал
- •2.4. Объявления переменных и констант базовых типо
- •If implicit in int interface
- •Internal is lock long namespace
- •Volatile while
- •Глава 3. Операции и целочисленные выражения
- •3.1. Операции языка c#
- •3.2. Операции присваивания и оператор
- •3.4. Выражения с арифметическими операциями
- •3.6. Переполнения при операциях с целыми
- •Глава 4. Выражения с операндами базовых
- •4.1. Автоматическое и явное приведение арифметических типов
- •4.2. Особые ситуации в арифметических выражениях
- •4.3. Логический тип и логические выражения
- •4.4. Выражения с символьными операндами
- •4.5. Тернарная (условная) операция
- •Глава 5. Типы с# как классы платформы .Net
- •5.1. Платформа .Net Framework и спецификация cts
- •5.2. Простые (базовые) типы c# как классы
- •5.3. Специфические методы и поля простых типов
- •Глава 6. Операторы
- •6.1. Общие сведения об операторах
- •6.2. Метки и оператор безусловного перехода
- •6.3. Условный оператор (ветвлений)
- •If (логическое выражение) оператор_1
- •6.4. Операторы цикла
- •6.5. Операторы передачи управления
- •If (условие) break;
- •6.6. Переключатель
- •Int ball; // оценка в баллах:
- •Глава 7. Массивы
- •7.1. Одномерные массивы
- •Int [ ] integers
- •Int number;
- •7.2. Массивы как наследники класса Array
- •7.3. Виды массивов и массивы многомерные
- •Int [,] dots;
- •Int size;
- •7.4. Массивы массивов и непрямоугольные массивы
- •Int size;
- •7.5. Массивы массивов и поверхностное копирование
- •Int size;
- •Int size;
- •Глава 8. Строки – объекты класса string
- •8.1. Строковые литералы
- •8.2. Строковые объекты и ссылки типа string
- •242.ToString()
- •8.3. Операции над строками
- •8.4. Некоторые методы и свойства класса String
- •8.5. Форматирование строк
- •8.6. Строка как контейне
- •8.7. Применение строк в переключателях
- •8.8. Массивы строк
- •8.8. Сравнение строк
- •Int static Compare (string, string)
- •Int static Compare (string, string, Boolean,CultureInfo)
- •If (string.Compare(res, hen, true,
- •8.9. Преобразования с участием строкового типа
- •38 Попугаев.
- •8.10. Аргументы метода Main( )
- •8.11. Неизменяемость объектов класса String
- •Глава 9. Методы c#
- •9.1. Методы–процедуры и методы-функции
- •9.2. Соотношение фиксированных параметров и аргументов
- •Int iPart;
- •9.3. Параметры с типами ссылок
- •Int[ ] temp;
- •Int[ ] temp;
- •9.4. Методы с переменным числом аргументов
- •VarParams(a, b, c);
- •9.5. Перегрузка методов
- •9.6. Рекурсивные методы
- •4*Fact (3); {
- •9.7. Применение метода Array.Sort()
- •Int имя_функции(тип параметр_1, тип параметр_2)
- •If(условие 2) return -1; // порядок соблюдён
- •Глава 10. Класс как совокупность статических
- •10.1. Статические члены класса
- •10.2. Поля классов (статические поля)
- •Int X; // поле объектов класса
- •10.3. Статические константы
- •10.4. Статические методы
- •10.5. Статический конструктор
- •10.6. Статические классы
- •Глава 11. Классы как типы
- •11.1. Объявление класса
- •11.2. Поля объектов
- •11.3. Объявления методов объектов
- •11.4. Пример класса и его объектов
- •Int count; // текущее показание
- •1 ' Counter.Count' is inaccessible due to its protection leve
- •11.5. Ссылка this
- •Int numb;
- •11.6. Конструкторы объектов класса
- •Int p; // порядок - инициализация по умолчанию
- •Void reduce() // Приведение числа к каноническому виду.
- •11.7. Деструкторы и финализаторы
- •Глава 12. Средства взаимодействия с объектами
- •12.1. Принцип инкапсуляции и методы объектов
- •12.2. Свойства классов
- •Internal, private, static, virtual, sealed, override, abstract, extern
- •Internal protected
- •Int p; // инициализация по умолчанию
- •Void reduce() // "Внутренний" для класса метод
- •12.3. Автореализуемые свойств
- •12.4. Индексаторы
- •Int[] days; // часы по дням недели
- •Int search(string str) { // поиск слова
- •12.5. Индексаторы, имитирующие наличие контейнера
- •Глава 13. Включение, вложение и наследование
- •13.1. Включение объектов классов
- •13.2. Вложение классов
- •13.3. Наследование классов
- •13.4. Доступность членов класса при наследовании
- •13.5. Методы при наследовании
- •13.6. Абстрактные методы и абстрактные классы
- •13.7. Опечатанные классы и методы
- •13.8. Применение абстрактых классов
- •Глава 14. Интерфейсы
- •14.1. Два вида наследования в ооп
- •14.2. Объявления интерфейсов
- •Interface имя_интерфейса
- •Interface iPublication { // интерфейс публикаций
- •Void write(); // готовить публикацию
- •Void read(); // читать публикацию
- •14.3. Реализация интерфейсов
- •Interface iPublication { // интерфейс публикаций
- •Void write(); // готовить публикацию
- •Void read(); // читать публикацию
- •Interface iSeries {
- •Void setBegin(); // восстановить начальное состояние
- •Int GetNext { get; } // вернуть очередной член ряда
- •Int this[int k] {get;} // вернуть к-й член ряда
- •Interface iSeries // интерфейс числовых рядов
- •14.4. Интерфейс как ти
- •Interface iGeo { // интерфейс геометрической фигуры
- •Void transform(double coef); // преобразовать размеры
- •Void display(); // вывести характеристики
- •Interface iGeo {
- •Void transform(double coef); // преобразовать размеры
- •Void display(); // вывести характеристики
- •Ira.Transform(3);
- •Ira.Transform(3);
- •14.5. Интерфейсы и наследование
- •Interface iPublication // интерфейс публикаций
- •Interface iBase
- •Interface iBase {
- •Глава 15. Перечисления и структуры
- •15.1. Перечисления
- •15.2. Базовый класс перечислений
- •IsDe fined"
- •15.3. Структуры
- •15.4. Упаковка и распаковка
- •If (obj is Struct1)
- •If (ob is PointS)
- •If (ob is Double)
- •15.5. Реализация структурами интерфейсов
- •Interface iShape {
- •Interface iShape
- •Information(ci);
- •Information(sp);
- •Interface iImage {
- •Void display();
- •Interface iImage
- •Void display();
- •Глава 16. Исключения
- •16.1. О механизме исключений
- •16.3. Свойства исключений
- •16.4. Управление программой с помощью исключений
- •X; // Вводимое число.
- •16.5. Исключения в арифметических выражениях
- •16.6. Генерация исключений
- •If (!double.TryParse(input, out u))
- •If (!double.TryParse(input, out u)
- •16.7. Пользовательские классы исключений
- •Глава 17. Делегаты и события
- •17.1. Синтаксис делегатов
- •17.2. Массивы делегатов
- •Int X, y; // положение робота на плоскости
- •17.3. Многоадресные (групповые) экземпляры делегатов
- •Virtual
- •17.4. Делегаты и обратные вызовы
- •17.5. Анонимные методы
- •17.6. События
- •Int size; // размер массива
- •Int[ ] ar; // ссылка на массив
- •Int temp;
- •Глава 18. Обобщения
- •18.1. Обобщения как средство абстракции
- •18.2. Декларации обобщённых классов "декларация
- •18.3. Ограничения типизирующих параметров
- •Intemface iComparable {
- •Int CompareTo (object p);
- •18.4. Обобщённые структуры "обобщённые структуры"
- •18.5. Обобщённые интерфейсы "обобщённый интерфейс"
- •Int add(t X, t y); // прототип метода
- •18.6. Обобщённые методы "обобщённые методы"
- •18.7. Обобщённые делегаты "обобщённые делегаты"
- •Virtual
- •InnerException
- •Interface
9.6. Рекурсивные методы
Рекурсивным называют метод, который прямо (непосредственно) или косвенно
вызывает самого себя. Метод называют косвенно рекурсивным "метод:косвенно
рекурсивный" , если он содержит обращение к другому методу, содержащему
прямой или косвенный вызов определяемого (первого) метода. В случае косвенной
рекурсивности по тексту определения метода его рекурсивность может быть не
видна. Если в теле метода явно используется обращение к этому методу, то имеет
место прямая рекурсия
"прямая рекурсия" . В этом случае говорят, что мето
д
самовызывающий (self-calling). Именно самовызывающие методы будем называть
рекурсивными, а для методов с косвенной рекурсией будем использовать термин
косвенно рекурсивные методы "метод:косвенно рекурсивный " .
Классический пример рекурсивного метода – функция для вычисления
факториала неотрицательного целого числа. На языке C# её можно записать таким
образом (программа 09_16.cs):
static long fact(int k)
{
if (k < 0) return 0;
if (k == 0 || k == 1) return 1;
return k * fact(k - 1);
}
Для отрицательного аргумента результат (по определению факториала) не
существует. В этом случае функция возвращает нулевое значение (можно было бы
возвращать, например, отрицательное значение). Для нулевого и единичного
аргумента, по определению факториала, возвращаемое значение равно 1. Если k>1,
то вызывается та же функция с уменьшенным на 1 значением и возвращаемое ею
значение умножается на текущее значение аргумента k. Тем самым организуется
вычисление произведения 1*2*3*...*(k-2)*(k-1)*k.
При проектировании рекурсивного метода нужно убедиться
– что он может завершить работу, т.е. невозможно возникновение зацикливания;
– что метод приводит к получению правильных результатов.
Для удовлетворения первого требования должны соблюдаться два правила:
В последовательности рекурсивных вызовов должен быть явный разрыв, то есть
самовызовы должны выполняться до тех пор, пока истинно значение
некоторого выражения, операнды которого изменяются от вызова к вызову.
При самовызовах должны происходить изменения параметров и эти изменения
после
конечного
числа
вызовов
должны
привести
к
нарушени
ю
проверяемого условия из пункта 1.
В нашем примере условием выхода из цепочки рекурсивных вызовов является
истинность выражения (k==0||k==1). Так как значение k конечно и параметр при
каждом следующем вызове уменьшается на 1, то цепочка самовызовов конечна.
Идя от конца цепочки, т.е. от 1!=1, к началу, можно убедиться, что все вызовы
работают верно и метод в целом работает правильно.
Иллюстрация цепочки самовызовов для метода вычисления факториала
fact (4)
{
4*Fact (3); {
{ 3*fact (2); {
{ 2*fact (1) {
} return 1;
}
Эффективность
рекурсивного
метода
зачастую
определяется
глубиной
рекурсии "глубина рекурсии" , т.е. количеством самовызовов в одной цепочке при
обращении к методу.
В качестве примера рекурсивной процедуры приведём метод, преобразующий
все цифры натурального числа в соответствующие им символы. Параметры метода
− целое число типа int и массив результатов char[ ]. Текст метода (программа
09_17.cs):
// Цифры натурального числа
static void numbers(int n, char[] ch)
{
int ind = (int)Math.Log10((double)n);
if (n < 10)
{
ch[ind] = (char)(n + (int)'0');
return;
}
numbers(n/10, ch);
ch[ind] = (char)(n%10 + (int)'0');
}
Ограничение: при обращении к методу необходимо, чтобы размер символьног
о
массива-аргумента для представления результатов был не менее разрядности числа.
В теле метода значение переменной ind на 1 меньше разрядности числового
значения аргумента int n. Если n<10, то ind=0 и выполняются операторы
ch[0] = (char)(n + (int)'0');
return;
Оператор return завершает выполнение очередного экземпляра процедуры и,
тем самым, прерывается цепочка её самовызовов. В противном случае выполняется
оператор numbers(n/10, ch);. Это самовызов, в котором параметр n уменьшен в 10
раз, то есть в анализируемом числе отброшен младший разряд. Обратите внимание,
что оператор ch[ind] = (char)(n%10 + (int)'0'); не выполнен – его исполнение
отложено до завершения вызова numbers(n/10, ch). А завершение, т.е. разрыв
цепочки самовызовов, наступит, когда значением аргумента будет число из одной
цифры. Это значение – старшая цифра исходного числа – преобразуется к
символьному виду и присваивается элементу ch[0]. После возврата из "самого
глубокого" обращения к методу выполняется оператор
ch[1] = (char)(n%10 + (int)'0');
Переменная n представляет в этом случае двухразрядное число и элементу
массива ch[1] присваивается изображение второй (слева) цифры числа и т.д..
Многие задачи, требующие циклического повторения операций, могут быть
представлены как рекурсивными, так и итерационными алгоритмами. Основной для
рекурсивного алгоритма является его самовызов, а для итерационного алгоритма –
цикл.
В качестве иллюстрации приведём рекурсивный метод (процедуру) для вывода
на консольный экран значений элементов символьного массива. Напомним, что при
создании символьного массива без явной инициализации его элементам по
умолчанию присваиваются значения ‘\0’. Выводятся элементы массива, начиная с
заданного. Окончание вывода – конец массива либо терминальный символ ‘\0’ в
качестве значения очередного элемента (программа 09_17.cs).
// Метод для печати элементов символьного массива
static void chPrint(char[] ch, int beg)
{
if (beg >= ch.Length-1 || ch[beg] == ‘\0’)
{
Console.WriteLine( );
return;
}
Console.Write(ch[beg] +" ");
chPrint(ch, beg + 1);
}
Первый параметр метода char[ ] ch – ссылка на обрабатываемый символьный
массив. Второй параметр int beg – индекс элемента, начиная с которого выводятся
значения. Условие выхода из метода – достижение конца массива beg >=
ch.Length-1 или значение ‘\0’ элемента с индексом beg. Если условие выхода не
достигнуто, выполняются операторы:
Console.Write(ch[beg] + " ");
chPrint(ch, beg + 1);
Тем самым первым выводится значение начального элемента массива и
происходит самовызов метода для следующего значения второго параметра. И т.д....
Вызов последнего отличного от ‘\0’ элемента массива завершает цепочку
рекурсивных обращений.
Пример использования приведённых методов (09_17.cs):
static void Main( )
{
int size = 9;
char[ ] simbols = new char[size];
numbers(13579, simbols);
chPrint(simbols, 0);
}
}
Результат выполнения программы:
13579
Обратите внимание, что при обращении к методу chPrint() второй аргумент
равен 0, т.е. вывод идёт с начала массива.