Добавить в цитаты Настройки чтения

Страница 55 из 188

// Вывести символьную строку в обратном порядке, используя рекурсию.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(s); Console.WriteLine(); }}Вот к какому результату приводит выполнение этого кода.

Исходная строка: Это тестПеревернутая строка: тсет отЭВсякий раз, когда вызывается метод DisplayRev(), в нем происходит проверкадлины символьной строки, представленной аргументом str. Если длина строки неравна нулю, то метод DisplayRev() вызывается рекурсивно с новой строкой, которая меньше исходной строки на один символ. Этот процесс повторяется до тех пор,пока данному методу не будет передана строка нулевой длины. После этого начнется раскручиваться в обратном порядке механизм всех рекурсивных вызовов методаDisplayRev(). При возврате из каждого такого вызова выводится первый символстроки, представленной аргументом str, а в итоге вся строка выводится в обратномпорядке.Рекурсивные варианты многих процедур могут выполняться немного медленнее,чем их итерационные эквиваленты из-за дополнительных затрат системных ресурсовна неоднократные вызовы метода. Если же таких вызовов окажется слишком много, тов конечном итоге может быть переполнен системный стек. А поскольку параметры илокальные переменные рекурсивного метода хранятся в системном стеке и при каждом новом вызове этого метода создается их новая копия, то в какой-то момент стекможет оказаться исчерпанным. В этом случае возникает исключительная ситуация,и общеязыковая исполняющая среда (CLR) генерирует соответствующее исключение.Но беспокоиться об этом придется лишь в том случае, если рекурсивная процедуравыполняется неправильно.Главное преимущество рекурсии заключается в том, что она позволяет реализоватьнекоторые алгоритмы яснее и проще, чем итерационным способом. Например, алгоритм быстрой сортировки довольно трудно реализовать итерационным способом.А некоторые задачи, например искусственного интеллекта, очевидно, требуют именнорекурсивного решения.При написании рекурсивных методов следует непременно указать в соответствующем месте условный оператор, например if, чтобы организовать возврат из метода без рекурсии. В противном случае возврата из вызванного однажды рекурсивногометода может вообще не произойти. Подобного рода ошибка весьма характерна дляреализации рекурсии в практике программирования. В этом случае рекомендуетсяпользоваться операторами, содержащими вызовы метода WriteLine(), чтобы следить за происходящим в рекурсивном методе и прервать его выполнение, если в немобнаружится ошибка.## Применение ключевого слова staticИногда требуется определить такой член класса, который будет использоваться независимо от всех остальных объектов этого класса. Как правило, доступ к члену класса организуется посредством объекта этого класса, но в то же время можно создатьчлен класса для самостоятельного применения без ссылки на конкретный экземпляробъекта. Для того чтобы создать такой член класса, достаточно указать в самом началеего объявления ключевое слово static. Если член класса объявляется как static, тоон становится доступным до создания любых объектов своего класса и без ссылки накакой-нибудь объект. С помощью ключевого слова static можно объявлять как переменные, так и методы. Наиболее характерным примером члена типа static служитметод Main(), который объявляется таковым потому, что он должен вызываться операционной системой в самом начале выполняемой программы.Для того чтобы воспользоваться членом типа static за пределами класса, достаточно указать имя этого класса с оператором-точкой. Но создавать объект для этого ненужно. В действительности член типа static оказывается доступным не по ссылке наобъект, а по имени своего класса. Так, если требуется присвоить значение 10 переменной count типа static, являющейся членом класса Timer, то для этой цели можновоспользоваться следующей строкой кода.

Timer.count = 10;Эта форма записи подобна той, что используется для доступа к обычным переменным экземпляра посредством объекта, но в ней указывается имя класса, а не объекта.Аналогичным образом можно вызвать метод типа static, используя имя класса иоператор-точку.Переменные, объявляемые как static, по существу, являются глобальными. Когда же объекты объявляются в своем классе, то копия переменной типа static несоздается. Вместо этого все экземпляры класса совместно пользуются одной и той жепеременной типа static. Такая переменная инициализируется перед ее применением в классе. Когда же ее инициализатор не указан явно, то она инициализируетсянулевым значением, если относится к числовому типу данных, пустым значением,если относится к ссылочному типу, или же логическим значением false, если относится к типу bool. Таким образом, переменные типа static всегда имеют какое-тозначение.Метод типа static отличается от обычного метода тем, что его можно вызывать поимени его класса, не создавая экземпляр объекта этого класса. Пример такого вызовауже приводился ранее. Это был метод Sqrt() типа static, относящийся к классуSystem.Math из стандартной библиотеки классов С#.Ниже приведен пример программы, в которой объявляются переменная и методтипа static.

// Использовать модификатор static.using System;



class StaticDemo { // Переменная типа static. public static int Val = 100; // Метод типа static. public static int ValDiv2() { return Val/2; }}

class SDemo { static void Main() { Console.WriteLine("Исходное значение переменной " + "StaticDemo.Val равно " + StaticDemo.Val); StaticDemo.Val = 8; Console.WriteLine("Текущее значение переменной" + "StaticDemo.Val равно " + StaticDemo.Val); Console.WriteLine("StaticDemo.ValDiv2(): " + StaticDemo.ValDiv2()); }}Выполнение этой программы приводит к следующему результату.

Исходное значение переменной StaticDemo.Val равно 100Текущее значение переменной StaticDemo.Val равно 8StaticDemo.ValDiv2(): 4Как следует из приведенного выше результата, переменная типа static инициализируется до создания любого объекта ее класса.На применение методов типа static накладывается ряд следующих ограничений.* В методе типа static должна отсутствовать ссылка this, поскольку такой метод не выполняется относительно какого-либо объекта.* В методе типа static допускается непосредственный вызов только других методов типа static, но не метода экземпляра из того самого же класса. Дело в том, что методы экземпляра оперируют конкретными объектами, а метод типа static не вызывается для объекта. Следовательно, у такого метода отсутствуют объекты, которыми он мог бы оперировать.* Аналогичные ограничения накладываются на данные типа static. Для метода типа static непосредственно доступными оказываются только другие данные типа static, определенные в его классе. Он, в частности, не может оперировать переменной экземпляра своего класса, поскольку у него отсутствуют объекты, которыми он мог бы оперировать.Ниже приведен пример класса, в котором недопустим метод ValDivDenom() типаstatic.

class StaticError { public int Denom = 3; // обычная переменная экземпляра public static int Val = 1024; // статическая переменная / Ошибка! Непосредственный доступ к нестатической переменной из статического метода недопустим. / static int ValDivDenom() { return Val/Denom; // не подлежит компиляции! }}В данном примере кода Denom является обычной переменной, которая недоступнаиз метода типа static. Но в то же время в этом методе можно воспользоваться переменной Val, поскольку она объявлена как static.Аналогичная ошибка возникает при попытке вызвать нестатический метод из статического метода того же самого класса, как в приведенном ниже примере.