
- •Язык c# и структура простой программы
- •Что такое .Net Framework
- •Что такое с#
- •Создание первой программы на с#
- •Пространство имен System
- •С# как язык Объектно-Ориентированного Программирования (ооп)
- •Состав и назначение файлов проекта
- •Структура сборки
- •Элементы языка c#
- •Базовый синтаксис с#
- •Переменные
- •Числа без знака
- •Числа со знаком
- •Числа для финансистов
- •Текстовые символы
- •Логический тип данных
- •Литералы с плавающей точкой
- •Символьные литералы
- •Строковые литералы
- •Выражения и операторы с#
- •Инициализация переменных и оператор присваивания
- •Значение в левой части
- •Ввод данных с консоли
- •Математические операторы
- •Вычисление остатка при целочисленном делении
- •Унарные операторы
- •Унарное логическое отрицание
- •Составные операторы
- •Поразрядные операторы
- •Поразрядное логическое и
- •Поразрядное логическое или
- •Поразрядное логическое исключающее или
- •Унарная поразрядная операция дополнения
- •Поразрядный сдвиг
- •Пример использования поразрядных операторов
- •Логические операторы
- •Операторы отношения
- •Приоритеты операторов
- •Исполнители алгоритмов
- •Исполнитель Чертежник
- •Исполнитель Робот
- •Компьютер как формальный исполнитель алгоритмов
- •Блок-схема
- •Псевдокод
- •Необходимые свойства алгоритма
- •Понятие о временной сложности алгоритма
- •Примеры
- •Правила для определения сложности
- •Тернарный условный оператор
- •Применение логических операций
- •Конструкция выбора
- •Примеры применения оператора выбора
- •Объединение меток case
- •Пропущенный break
- •Пример программы: Простые числа
- •Пример обработки одномерного массива чисел
- •Использование генератора случайных данных
- •Использование оператора foreach
- •Типы задач обработки одномерных числовых массивов
- •Поиск элемента в массиве
- •Многомерные массивы
- •Базовые операции со строками
- •Методы типа string
- •Копирование и клонирование строк
- •Конкатенация строк
- •Извлечение подстроки
- •Вставка подстроки
- •Замена символов и строк
- •Удаление символов из строки
- •Удаление незначащих пробелов
- •Преобразование к верхнему и нижнему регистру
- •Выравнивание по левому и правому краю поля
- •Объединение массива строк
- •Разбор строки
- •Сравнение строк
- •Форматирование текстовых строк
- •Функции
- •Описание и использование функций
- •Возвращаемые значения
- •Передача параметров
- •Выходные параметры
- •Область действия переменных
- •Область действия переменных и управляющие конструкции
- •Рекурсия
- •Параметры функции Main()
- •Перегрузка функций
- •Перечислимый тип
- •Определение перечислимых типов
- •Структуры
- •Описание структур
- •Использование структур в качестве параметров функций
- •Использование структур в качестве возвращаемых значений функций
- •Функции структур
- •Файлы и потоки
- •Основные классы ввода и вывода
- •Классы для работы с потоками
- •Классы для работы с потоками текстовых символов
- •Работа с текстовыми файлами
- •Запись двоичных данных
- •Пример программы для работы с двоичным файлом
- •Просмотр содержимого каталога
- •Получение информации о каталоге
Перегрузка функций
Несмотря на удобство использования функций, при их разработке могут возникнуть определенные проблемы. Если необходимо выполнить аналогичные операции с переменными различных типов, то приходится иметь отдельные функции для работы с каждым типом переменных. Этим функциям можно дать различные имена, но поскольку суть выполняемых операций при этом остается похожей, то логичнее, чтобы имена этих функций совпадали.
В C# заложена возможность создавать одновременно несколько функций, имеющих одинаковое имя, но работающих с параметрами различных типов. Этот механизм называется перегрузкой функций и использует понятие сигнатуры функции для выбора нужной функции из нескольких, имеющих одинаковые имена.
Под сигнатурой функции понимается количество ее параметров, их типы и модификаторы, но не возвращаемое значение. Рассмотрим пример использования в одной программе двух похожих функций. Ранее рассматривалось использование функции поиска максимального элемента в массиве:
static int maxValue(int[] intArray)
{
int maxVal = intArray[0];
for (int i = 1; i < intArray.Length; i++)
{
if (intArray[i] > maxVal)
maxVal = intArray[i];
}
return maxVal;
}
Эта функция может использоваться только для массивов значений типа int. Мы могли бы создать функции с другими именами, предназначенные для работы с параметрами других типов, переименовав вышеприведенную функцию как-нибудь вроде IntArrayMaxValue() И добавив функции наподобие DoubleArrayMaxValue() для работы с данными с плавающей точкой. Вместо этого в качестве альтернативы мы можем просто включить в нашу программу следующую функцию:
static double maxValue(double[] doubleArray)
{
double maxVal = doubleArray[0];
for (int i = 1; i < doubleArray.Length; i++)
{
if (doubleArray[i] > maxVal)
maxVal = doubleArray[i];
}
return maxVal;
}
Разница между двумя функциями заключается в том, что эта функция работает со значениями типа double. Имя функции — MaxVaiue() — оказывается тем же самым, однако сигнатура (это принципиально) отличается. Было бы ошибкой описать две функции с одинаковым именем и одинаковой сигнатурой, однако поскольку в данном случае сигнатуры различны, то все нормально.
Теперь у нас имеются две версии функции MaxValue(), которые принимают массивы типа int и массивы типа double и возвращают максимальное значение типа int или типа double соответственно.
Красота такой формы программы в том, что не требуется явно указывать, которую из этих двух функций мы собираемся использовать. Мы просто задаем массив-параметр, и это приводит к выполнению того варианта, который соответствует типу используемого параметра.
При использовании перегруженных функций заметную помощь может оказать редактор кода Visual Studio. Если в нашем приложении имеются две одноименные функции, описанные выше, и мы наберем это имя, например, в Main(), то выведет доступные варианты перегрузки данной функции. Набираем в редакторе оператор, где вызывается перегруженная функция и открываем скобку:
double result = maxValue(
Редактор Visual Studio выведет информацию по обеим версиям функции maxValue(), которые мы можем просмотреть с помощью кнопок "стрелка вверх" и "стрелка вниз":
При перегрузке функций учитываются все аспекты, касающиеся их сигнатур. Существует возможность, например, описать две различные функции, одна из которых принимает параметры по значению, а другая, соответственно, по ссылке:
static void showDouble(ref int val)
{
. . .
}
static void showDouble(int val)
{
. . .
}
Выбор используемой версии осуществляется исключительно на основании того, имеется ли в обращении к функции ключевое слово ref. При следующем вызове будет использован вариант, в котором параметр передается по ссылке:
showDouble(ref val);
А такой вызов позволит передать параметр по значению:
showDouble(val);
Аналогичным образом можно описывать функции, отличающиеся числом требующихся им параметров и т. п. Попытка описать две функции, различающиеся только типом возвращаемого значения, обречена на неудачу. Например, создадим в программе следующие функции:
static double func(int x)
{
. . .
}
static int func(int x)
{
. . .
}
При компиляции второй функции получим ошибку: в классе Program уже определена функция func() с такими же типами параметров.