
- •Язык c# и структура простой программы
- •Что такое .Net Framework
- •Что такое с#
- •Создание первой программы на с#
- •Пространство имен System
- •С# как язык Объектно-Ориентированного Программирования (ооп)
- •Состав и назначение файлов проекта
- •Структура сборки
- •Элементы языка c#
- •Базовый синтаксис с#
- •Переменные
- •Числа без знака
- •Числа со знаком
- •Числа для финансистов
- •Текстовые символы
- •Логический тип данных
- •Литералы с плавающей точкой
- •Символьные литералы
- •Строковые литералы
- •Выражения и операторы с#
- •Инициализация переменных и оператор присваивания
- •Значение в левой части
- •Ввод данных с консоли
- •Математические операторы
- •Вычисление остатка при целочисленном делении
- •Унарные операторы
- •Унарное логическое отрицание
- •Составные операторы
- •Поразрядные операторы
- •Поразрядное логическое и
- •Поразрядное логическое или
- •Поразрядное логическое исключающее или
- •Унарная поразрядная операция дополнения
- •Поразрядный сдвиг
- •Пример использования поразрядных операторов
- •Логические операторы
- •Операторы отношения
- •Приоритеты операторов
- •Исполнители алгоритмов
- •Исполнитель Чертежник
- •Исполнитель Робот
- •Компьютер как формальный исполнитель алгоритмов
- •Блок-схема
- •Псевдокод
- •Необходимые свойства алгоритма
- •Понятие о временной сложности алгоритма
- •Примеры
- •Правила для определения сложности
- •Тернарный условный оператор
- •Применение логических операций
- •Конструкция выбора
- •Примеры применения оператора выбора
- •Объединение меток case
- •Пропущенный break
- •Пример программы: Простые числа
- •Пример обработки одномерного массива чисел
- •Использование генератора случайных данных
- •Использование оператора foreach
- •Типы задач обработки одномерных числовых массивов
- •Поиск элемента в массиве
- •Многомерные массивы
- •Базовые операции со строками
- •Методы типа string
- •Копирование и клонирование строк
- •Конкатенация строк
- •Извлечение подстроки
- •Вставка подстроки
- •Замена символов и строк
- •Удаление символов из строки
- •Удаление незначащих пробелов
- •Преобразование к верхнему и нижнему регистру
- •Выравнивание по левому и правому краю поля
- •Объединение массива строк
- •Разбор строки
- •Сравнение строк
- •Форматирование текстовых строк
- •Функции
- •Описание и использование функций
- •Возвращаемые значения
- •Передача параметров
- •Выходные параметры
- •Область действия переменных
- •Область действия переменных и управляющие конструкции
- •Рекурсия
- •Параметры функции Main()
- •Перегрузка функций
- •Перечислимый тип
- •Определение перечислимых типов
- •Структуры
- •Описание структур
- •Использование структур в качестве параметров функций
- •Использование структур в качестве возвращаемых значений функций
- •Функции структур
- •Файлы и потоки
- •Основные классы ввода и вывода
- •Классы для работы с потоками
- •Классы для работы с потоками текстовых символов
- •Работа с текстовыми файлами
Область действия переменных и управляющие конструкции
В предыдущем разделе утверждалось, что область действия переменных распространяется на блок кода, в котором они описаны, и на все блоки, непосредственно в него вложенные. Это также применимо и к другим типам блоков, например, к программным конструкциям, использующим ветвление и циклы. Рассмотрим следующий фрагмент кода:
int i;
for (i = 0; i < 10; i++)
{
string text = "Строка " + Convert.ToString(i);
Console.WriteLine("{0}", text);
}
Console.WriteLine("Последняя строка, напечатанная в цикле: {0}", text);
В данной программе строковая переменная text является локальной для цикла for. Такой код не пройдет компиляцию, поскольку в обращении к Console.WriteLine(), которое происходит вне этого цикла, делается попытка использовать переменную text, область действия которой не распространяется за пределы цикла. Изменим код следующим образом:
int i;
string text;
for (i = 0; i < 10; i++)
{
text = "Строка " + Convert.ToString(i);
Console.WriteLine("{0}", text);
}
Console.WriteLine("Последняя строка, напечатанная в цикле: {0}", text);
Этот код также недопустим. Причина кроется в том, что переменные должны не только описываться, но и инициализироваться до того, как они будут использоваться, а переменная text инициализируется только в цикле for. Значение, присвоенное переменной text, при выходе из цикла будет утрачено. Однако можно модифицировать код еще раз:
int i;
string text = "";
for (i = 0; i < 10; i++)
{
text = "Строка " + Convert.ToString(i);
Console.WriteLine("{0}", text);
}
Console.WriteLine("Последняя строка, напечатанная в цикле: {0}", text);
На этот раз переменная text инициализирована вне цикла, и мы имеем доступ к ее значению. Результат выполнения этой программы приведен ниже. В данном случае значение, присвоенное переменной text внутри цикла, оказывается доступным и вне этого цикла:
В качестве заключительного замечания необходимо упомянуть о "наилучшей практике". Вообще говоря, лучше всего объявлять и инициализировать переменные перед теми блоками кода, в которых они используются. Исключением из этого правила могут быть переменные циклов, объявление которых является составной частью самого цикла. Например:
for(int i = 0; i < 10; i++)
{
. . .
}
В данном случае, переменная i является локализованной в блоке кода, представляющем собой цикл, но это и хорошо, если не требуется доступ к значению счетчика цикла из внешнего кода. Если в цикле используется оператор break, и необходимо узнать, с каким значением счетчика завершился цикл, то объявлять эту переменную придется до цикла.
Рекурсия
Иногда возникают ситуации, когда в теле функции необходимо вызвать экземпляр этой же функции. Такое явление называется рекурсией.
Использование рекурсии позволит вам в некоторых случаях избежать сложных циклов и проверок. Типичным примером использования рекурсии является функция нахождения факториала числа. Программа, использующая такую функцию, может выглядеть следующим образом:
using System;
namespace Factorial
{
class Program
{
static void Main(string[] args)
{
for (int i = 1; i < 10; i++)
{
Console.WriteLine("Факториал числа {0} = {1}",
i, Factorial(i));
}
Console.ReadLine();
}
static int Factorial(int x)
{
if(x == 1)
return 1;
else
return x * Factorial(x - 1);
}
}
}
Результатом работы этой программы будет:
Функция Factorial является рекурсивной. Ее построение отличается от построения обычной функции, она не содержит циклов и других сложных конструкций. Она содержит всего одно условие, одну операцию умножения и вызов самой себя. Логика ее работы также несложна.
Если функция приняла значение, отличное от единицы, то вызывает саму себя и передает значение, на единицу меньшее. Так происходит до тех пор, пока переданное значение не станет равным единице. После того как функция получит значение 1 в качестве параметра, начнется выход из всех экземпляров вызванных функций Factorial и возврат произведения параметра функции и возвращенного значения.
Допустим, мы ищем факториал числа 5. Тогда передача параметров экземплярам функции Factorial идет в такой последовательности: 5, 4, 3, 2, 1. А последовательность возвращаемых значений будет следующей: 1, 2, 6, 24, 120. Результатом работы функции станет число 120.
Однако рекурсия имеет и недостатки. При вызове каждого экземпляра функции выделяется дополнительное место в оперативной памяти для передачи параметров и размещения возвращаемых значений, что при большой глубине рекурсии может привести к переполнению стека. Допустим, из-за случайной ошибки в последней строке функции написано:
return x * Factorial(x + 1);
В этом случае при первом же вызове с параметром, отличным от 1, функция не сможет завершиться, и будет бесконечно вызывать себя, пока не переполнится стек (т.е. возникнет исключительная ситуация):
При правильном использовании рекурсия – очень удобная и эффективная вещь. Она позволяет решать большие и достаточно сложные задачи за счет использования небольших и часто изящных функций, вызывающих самих себя.