Урок 8. Методы — свои функции
Когда программа растёт, в ней появляются повторяющиеся куски: напечатать разделитель, найти большее из двух чисел, проверить ввод. Их выносят в методы — именованные блоки кода, которые можно вызывать сколько угодно раз. В других языках то же самое называют функциями.
Первый метод
В этом уроке программы написаны в классической форме — с классом Program и методом Main (урок Установка .NET и первая программа). Свои методы пишутся рядом с Main, внутри того же класса:
class Program
{
static void Main()
{
PrintLine(10);
int s = Sum(2, 3);
Console.WriteLine(s);
Console.WriteLine(Max(4, 9));
PrintLine(10);
}
static void PrintLine(int length)
{
Console.WriteLine(new string('-', length));
}
static int Sum(int a, int b)
{
return a + b;
}
static int Max(int a, int b) => a > b ? a : b;
}----------
5
9
----------Из чего состоит заголовок static int Sum(int a, int b):
static— метод принадлежит самому классу, и его можно вызвать изMainбез создания объекта (что такое объекты — в следующем уроке). Пока все методы пишите соstatic.int— тип результата.voidвместо него значит «ничего не возвращает», как уPrintLine.Sum— имя. Методы в C# называют с большой буквы.(int a, int b)— параметры: значения, которые метод получает при вызове. В вызовеSum(2, 3)числа 2 и 3 — аргументы, они попадают вaиb.return a + b;— вернуть результат и сразу выйти из метода.
Если метод — одно выражение, его можно записать стрелкой: => a > b ? a : b;, как Max. new string('-', length) создаёт строку из length одинаковых символов.
return
В методе с типом результата return должен срабатывать на любом пути выполнения. Иначе — ошибка not all code paths return a value (CS0161):
static string Sign(int n)
{
if (n > 0)
{
return "плюс";
}
if (n < 0)
{
return "минус";
}
return "ноль";
}Без последней строки компилятор не знает, что вернуть при n == 0, и программа не соберётся. В методе void можно написать return; без значения — чтобы выйти раньше.
Параметры по умолчанию и именованные аргументы
У параметра можно задать значение по умолчанию — тогда его разрешено не передавать. А аргументы можно называть по именам и писать в любом порядке:
class Program
{
static void Main()
{
Greet("Анна");
Greet("Борис", "Здравствуйте");
Greet(greeting: "Добрый день", name: "Вера");
}
static void Greet(string name, string greeting = "Привет")
{
Console.WriteLine($"{greeting}, {name}!");
}
}Привет, Анна!
Здравствуйте, Борис!
Добрый день, Вера!Перегрузка
В одном классе может быть несколько методов с одним именем, если у них разные параметры. Это перегрузка: компилятор выбирает нужный вариант по аргументам.
class Program
{
static void Main()
{
Console.WriteLine(Area(5));
Console.WriteLine(Area(3, 4));
}
static int Area(int side) => side * side;
static int Area(int width, int height) => width * height;
}25
12Console.WriteLine — тоже перегруженный метод: у него есть варианты для строк, чисел, символов и других типов.
Как передаются аргументы: ref и out
Обычно метод получает копию значения, и изменения внутри метода снаружи не видны. Ключевое слово ref передаёт саму переменную, out — переменную, в которую метод обязан положить результат:
class Program
{
static void Main()
{
int n = 5;
AddTen(n);
Console.WriteLine(n);
AddTenRef(ref n);
Console.WriteLine(n);
Divide(17, 5, out int quotient, out int remainder);
Console.WriteLine($"{quotient} и {remainder} в остатке");
}
static void AddTen(int x)
{
x += 10;
}
static void AddTenRef(ref int x)
{
x += 10;
}
static void Divide(int a, int b, out int q, out int r)
{
q = a / b;
r = a % b;
}
}5
15
3 и 2 в остаткеref и out пишут и в объявлении, и в вызове. С out вы уже встречались: int.TryParse(text, out int value) возвращает bool, а само число кладёт в out-переменную.
Массивы, списки и объекты классов передаются по ссылке на них: если метод поменяет элемент массива, это будет видно и снаружи.
params и рекурсия
params позволяет передать сколько угодно аргументов — внутри метода они видны как массив. А рекурсия — это когда метод вызывает сам себя:
class Program
{
static void Main()
{
Console.WriteLine(SumAll(1, 2, 3));
Console.WriteLine(SumAll(10, 20, 30, 40));
Console.WriteLine(Factorial(5));
Console.WriteLine(Factorial(20));
}
static int SumAll(params int[] numbers)
{
int sum = 0;
foreach (int x in numbers)
{
sum += x;
}
return sum;
}
static long Factorial(int n)
{
if (n <= 1)
{
return 1;
}
return n * Factorial(n - 1);
}
}6
100
120
2432902008176640000У рекурсии обязательно должно быть условие остановки (n <= 1), иначе вызовы не кончатся, и программа упадёт с переполнением стека — памяти под вызовы методов (Stack overflow). Факториал быстро растёт, поэтому результат — long: 20! уже не помещается в int.
Методы в программе без Main
В программе на операторах верхнего уровня методы пишут после основного кода:
Console.WriteLine(Square(7));
Console.WriteLine(Square(12));
int Square(int x)
{
return x * x;
}49
144Такие методы называются локальными функциями. Работают они так же, но с ограничением: два метода с одним именем (перегрузка) здесь не получатся. Если нужна перегрузка, пишите классическую форму с классом.
Частые ошибки
not all code paths return a value(CS0161) — есть путь, на котором метод ничего не возвращает.An object reference is required for the non-static field, method, or property(CS0120) — метод вызывают изstatic Main, а сам он безstatic.- Вызвали метод, а результат не сохранили:
Sum(2, 3);ничего не печатает. НужноConsole.WriteLine(Sum(2, 3));илиint s = Sum(2, 3);. - Ждут, что метод изменит переданное число, — а он меняет копию. Верните новое значение через
returnили используйтеref.
Коротко
- Метод:
static тип Имя(параметры) { ... return значение; };void— ничего не возвращает. - Параметры могут иметь значения по умолчанию, аргументы — передаваться по именам.
- Перегрузка — несколько методов с одним именем и разными параметрами.
- Числа передаются копией;
refиoutпередают саму переменную. - В программе без
Mainметоды пишут после основного кода — это локальные функции, без перегрузки.