Когда метод вызывает самого себя, в системном стеке распределяется память для новых локальных переменных и параметров, и код метода выполняется с этими новыми переменными и параметрами с самого начала. При рекурсивном вызове метода не создается его новая копия, а лишь используются его новые аргументы. А при возврате из каждого рекурсивного вызова старые локальные переменные и параметры извлекаются из стека, и выполнение возобновляется с точки вызова в методе. Рекурсивные методы можно сравнить по принципу действия с постепенно сжимающейся и затем распрямляющейся пружиной.
Ниже приведен еще один пример рекурсии для вывода символьной строки в обратном порядке. Эта строка задается в качестве аргумента рекурсивного метода
DisplayRev().
// Вывести символьную строку в обратном порядке, используя рекурсию.
using System;
class RevStr {
// Вывести символьную строку в обратном порядке, public void DisplayRev(string str) { if (str.Length > 0)
DisplayRev(str.Substring(1, str.Length-1)); else
return;
Console.Write(str[0]);
}
}
class RevStrDemo { static void Main() {
string s = "Это тест"; ,
RevStr rsOb = new RevStr ();
Console.WriteLine("Исходная строка: " + s);
Console.Write("Перевернутая строка: "); rsOb.DisplayRev(sf;
Console.WriteLine();
}
}
Вот к какому результату приводит выполнение этого кода.
Исходная строка: Это тест Перевернутая строка: тсет отЭ
Всякий раз, когда вызывается метод DisplayRev (), в нем происходит проверка длины символьной строки, представленной аргументом str. Если длина строки не равна нулю, то метод DisplayRev () вызывается рекурсивно с новой строкой, которая меньше исходной строки на один символ. Этот процесс повторяется до тех пор, пока данному методу не будет передана строка нулевой длины. После этого начнется раскручиваться в обратном порядке механизм всех рекурсивных вызовов метода DisplayRev (). При возврате из каждого такого вызова выводится первый символ строки, представленной аргументом s t г, а в итоге вся строка выводится в обратном порядке.
Рекурсивные варианты многих процедур могут выполняться немного медленнее, чем их итерационные эквиваленты из-за дополнительных затрат системных ресурсов на неоднократные вызовы метода. Если же таких вызовов окажется слишком много, то в конечном итоге может быть переполнен системный стек. А поскольку параметры и локальные переменные рекурсивного метода хранятся в системном стеке и при каждом новом вызове этого метода создается их новая копия, то в какой-то момент стек может оказаться исчерпанным. В этом случае возникает исключительная ситуация, и общеязыковая исполняющая среда (CLR) генерирует соответствующее исключение. Но беспокоиться об этом придется лишь в том случае, если рекурсивная процедура выполняется неправильно.
Главное преимущество рекурсии заключается в том, что она позволяет реализовать некоторые алгоритмы яснее и проще, чем итерационным способом. Например, алгоритм быстрой сортировки довольно трудно реализовать итерационным способом. А некоторые задачи, например искусственного интеллекта, очевидно, требуют именно рекурсивного решения.
При написании рекурсивных методов следует непременно указать в соответствующем месте условный оператор, например i f , чтобы организовать возврат из метода без рекурсии. В противном случае возврата из вызванного однажды рекурсивного метода может вообще не произойти. Подобного рода ошибка весьма характерна для реализации рекурсии в практике программирования. В этом случае рекомендуется пользоваться операторами, содержащими вызовы метода WriteLine () , чтобы следить за происходящим в рекурсивном методе и прервать его выполнение, если в нем обнаружится ошибка.
Применение ключевого слова static
Иногда требуется определить такой член класса, который будет использоваться независимо от всех остальных объектов этого класса. Как правило, доступ к члену класса организуется посредством объекта этого класса, но в то же время можно создать член класса для самостоятельного применения без ссылки на конкретный экземпляр объекта. Для того чтобы создать такой член класса, достаточно указать в самом начале его объявления ключевое слово static. Если член класса объявляется как static, то он становится доступным до создания любых объектов своего класса и без ссылки на какой-нибудь объект. С помощью ключевого слова static можно объявлять как переменные, так и методы. Наиболее характерным примером члена типа static служит метод Main () , который объявляется таковым потому, что он должен вызываться операционной системой в самом начале выполняемой программы.
Читать дальше