
- •Глава 9. Методы с#
- •9.1. Методы-процедуры и методы-функции
- •9.2. Соотношение фиксированных параметров и аргументов
- •9.3. Параметры с типами ссылок
- •1 4 8 2 4 9 3
- •1 2 3 4 4 8 9
- •1 4 9 16 25 36
- •9.4. Методы с переменным числом аргументов
- •9.5. Перегрузка методов
- •9.6. Рекурсивные методы
- •9.7. Применение метода Array.Sort()
Глава 9. Методы с#
9.1. Методы-процедуры и методы-функции
Как писал Никлаус Вирт, программы это алгоритмы + структуры данных. Данные в С# - это поля объектов и статические поля классов. Алгоритмы (то есть функциональность программ) представляются с помощью методов класса и методов его объектов. В языке С# методы не существуют вне классов. Нестатические методы - реализуют функциональность объектов. Статические методы - обеспечивают функциональность классов и тем самым программы в целом.
Для определения методов и при их вызове в С# не используются никакие служебные слова. В "языках древности", таких как ФОРТРАН или КОБОЛ, и в некоторых более современных языках, не входящих в семейство Си-образных, для обозначения подпрограмм, функций, процедур используются специальные термины, включаемые в список служебных слов соответствующего языка. (FUNCTION, SUBROUTINE - в Фортране, procedure в Паскале и т.д.). Для вызова подпрограмм в Фортране используется конструкция со служебным словом CALL и т.п.
В языках, ведущих своё происхождение от языка Си (C++, Java, C# и др.) при определении функций и для обращения к ним специальные термины не применяются. Это в полной мере относится и к методам языка С#. Однако синтаксически и семантически методы С# можно разделить на процедуры и функции. Это деление не особенно жёсткое и в ряде случаев метод может играть две роли - и процедуры и функции.
В упрощённом варианте формат декларации метода, который может играть роль как процедуры, так и функции, можно представить так:
модификаторы_методаopt тип_возвращаемого_значения имя_метода (спецификация_параметров)
{ операторы_тела_метода }
Индекс opt указывает на необязательность модификаторов метода. Фигурные скобки ограничивают тело метода, часть объявления перед телом метода называют заголовком метода. Минимальная конструкция, применяемая для обращения к методу:
имя_метода(список_аргументов);
В общем случае имя метода при обращении дополняется префиксами, указывающими на принадлежность метода пространству имён, конкретному классу или реально существующему объекту.
Метод-процедура отличается от метода-функции следующими свойствами:
В качестве типа возвращаемого значения используется void, т.е. процедура не возвращает в точку вызова никакого результата.
В теле процедуры может отсутствовать оператор возврата return, a когда он присутствует, то в нём нет выражения для вычисления возвращаемого значения. Если оператор return отсутствует, то точка выхода из процедуры (из метода) расположена за последним оператором тела метода.
Для обращения к методу-процедуре используется только оператор вызова метода:
имя_метода (список_аргументов);
Метод-функция:
В качестве типа возвращаемого значения используется тип ссылки или тип значения. Таким образом, функция всегда возвращает в точку вызова результат.
В теле функции всегда присутствует, по крайней мере, один оператор возврата:
return выражение;
Выражение определяет возвращаемый функцией результат.
Обращение к методу-функции может использоваться в качестве операнда подходящего выражения. Термин "подходящего" относится к необходимости согласования операндов в выражении по типам.
Если результат, возвращаемый методом-функцией, по каким-то причинам не нужен в программе, а требуется только выполнение операторов тела функции, то обращение к ней может оформляться как отдельный оператор:
имя_метода (список_аргументов);
В этом случае функция выступает в роли процедуры.
Когда вы знакомитесь с некоторым методом (или пишете свой метод), очень важно понимать откуда метод берёт (или будет брать) исходные данные и куда (и как) метод отправляет результаты своей работы.
Исходные данные могут быть получены:
Через аппарат параметров метода;
Как глобальные по отношению к методу;
От внешних устройств (потоки ввода, файловые потоки).
Результаты метод может передавать:
В точку вызова как возвращаемое функцией значение;
В глобальные по отношению к методу объекты;
Внешним устройствам (потоки вывода, файловые потоки);
Через аппарат параметров метода.
Глобальные по отношению к методу объектами в С# являются статические поля класса, в котором, метод определён, и поля (статические) других классов, непосредственный доступ к которым имеет метод. Обмены через глобальные объекты являются нарушением принципов инкапсуляции и обычно в реальных разработках запрещены или не рекомендуются.
Обмены со стандартными потоками ввода-вывода, поддерживаемые средствами класса Console, нам уже знакомы. Для организации обменов с файловыми потоками используются те средства библиотеки классов, которые мы ещё не рассматривали. Сосредоточимся на особенностях обменов через аппарат параметров.
При определении метода в его заголовке размещается спецификация параметров (возможно пустая) - разделённая запятыми последовательность спецификаторов параметров. Каждый спецификатор имеет вид:
модификатор тип_параметра имя_параметра
Модификатор может отсутствовать или имеет одну из следующих форм: ref, out, params.
Ограничений на тип параметра не накладывается. Параметр может быть предопределённого типа (базовые типы, строки, object); перечислением; структурой; классом; массивом; интерфейсом; делегатом.
Имя параметра это идентификатор, который выбирает программист – автор метода. Область видимости и время существования параметра ограничиваются заголовком и телом метода. Таким образом, параметры не видны и недоступны для кода, который не размещён в теле метода.
Стандарт С# отмечает существование четырёх видов параметров:
параметры, передаваемые по значениям;
параметры, передаваемые по ссылкам (ref);
выходные параметры (out);
массив-параметр (params).
Параметры первых трёх видов в Стандарте С# называют фиксированными параметрами. Спецификация фиксированного параметра включает необязательный модификатор ref или out, обозначение типа и идентификатор (имя параметра).
Список параметров представляет собой возможно пустую последовательность разделённых запятыми спецификаторов параметров, из которых только последний может быть массивом-параметром, имеющим модификатор params.
Модификаторы метода необязательны, но их достаточно много. Вот их список пока без комментариев:
new, public, protected, internal, private, static, virtual, sealed, override, abstract, extern.
В данной главе мы будем рассматривать только методы классов (не объектов). В декларацию каждого метода класса входит модификатор static и такой метод называют статическим методом класса.
Чтобы продемонстрировать некоторые возможности и отличия метода-процедуры от метода-функции, рассмотрим следующую программу
// 09_01.cs - Статические методы - процедура и функция
static void print(string line)
{
Console.WriteLine("Длина строки: " + line.Length);
Console.WriteLine("Значение строки: " + line);
}
static string change(string str)
{
char[] rev = str.ToCharArray();
Array.Reverse(rev);
return new string(rev);
}
static void Main()
{
string numbers = "123456789";
print(numbers);
numbers = change(numbers);
print(numbers);
}
Результат выполнения программы:
Длина строки: 9
Значение строки: 123456789
Длина строки: 9
Значение строки: 987654321
В классе Program три статических метода. Метод print() получает исходные данные в виде строки-параметра и выводит длину и значение этой строки. В точку вызова метод print() ничего не возвращает - это процедура.
Метод change() - это функция. Он получает в качестве параметра строку, формирует её перевёрнутое значение и возвращает в точку вызова этот результат.
В функции Main() определена ссылка numbers на строку "123456789". В отдельном операторе вызван метод-процедура print(numbers), который выводит сведения о строке, именованной ссылкой numbers. Затем та же ссылка использована в качестве аргумента метода-функции change(). Обращение к этому методу размещено в правой части оператора присваивания, поэтому возвращаемый методом change() результат становится новым значением ссылки numbers. Повторное обращение к методу print() иллюстрирует изменения.