4_Методы

Урок 4 : Методы

О чём этот урок

Методы - именованные блоки кода, которые можно вызывать многократно. Они делают программу структурированной и избавляют от повторений.


1. Что такое метод

Метод - это как рецепт блюда: написан один раз, а приготовить по нему (вызвать) можно сколько угодно раз, передавая разные ингредиенты (аргументы).

Именованный блок кода, выполняющий определённую задачу. Объявляется один раз, вызывается сколько угодно раз.

// Без методов - дублирование кода:
Console.WriteLine("Добро пожаловать!");
Console.WriteLine("Игра началась.");
Console.WriteLine("Добро пожаловать!");  // снова то же самое
Console.WriteLine("Игра началась.");

// С методом - один раз объявили, вызываем сколько нужно:
void ShowWelcome()
{
    Console.WriteLine("Добро пожаловать!");
    Console.WriteLine("Игра началась.");
}

ShowWelcome(); // первый вызов
ShowWelcome(); // второй вызов

2. Сигнатура метода

Сигнатура - это «паспорт» метода, по которому компилятор его идентифицирует.

[модификаторы] возвращаемый_тип ИмяМетода(тип параметр1, тип параметр2, ...)
      ↓               ↓               ↓              ↓
    static           int           Add          (int a, int b)
Часть Описание Пример
Модификаторы Область доступа и поведение public, static, private
Возвращаемый тип Тип значения, которое метод возвращает int, string, void
Имя метода Идентификатор Add, GetName, PrintInfo
Параметры Входные данные (может быть пустым) (int a, int b), ()
// Полный пример сигнатуры
public static int Add(int a, int b)
{
    return a + b;
}

3. Метод Main - точка входа

Main - специальный метод, с которого начинается выполнение программы. Вызывается автоматически операционной системой.

// Стандартная точка входа
static void Main(string[] args)
{
    // всё начинается здесь
}

// Современный вариант (без явного класса и Main)
// - работает в C# 9+, но в Unity не используется
В Unity нет Main

В Unity точкой входа служат методы жизненного цикла MonoBehaviour: Awake(), Start(), Update(). Но принцип тот же - определённые методы вызываются автоматически.


4. Возвращаемый тип

Метод может возвращать значение с помощью оператора return.

// void - ничего не возвращает
void PrintGreeting(string name)
{
    Console.WriteLine($"Привет, {name}!");
    // return; // можно написать явно, но необязательно
}

// int - возвращает целое число
int Multiply(int a, int b)
{
    return a * b;  // return обязателен
}

// bool - возвращает логическое значение
bool IsEven(int number)
{
    return number % 2 == 0;
}

// string - возвращает строку
string GetStatus(int health)
{
    if (health > 50) return "Хорошо";
    if (health > 0)  return "Плохо";
    return "Мёртв";
}
return завершает метод немедленно

Код после return не выполнится. Это можно использовать для ранних выходов из метода.

void ProcessOrder(int quantity)
{
    if (quantity <= 0)
    {
        Console.WriteLine("Некорректное количество");
        return;  // выходим из метода, код ниже не выполнится
    }

    // обрабатываем заказ...
    Console.WriteLine($"Обработка {quantity} единиц");
}

5. Параметры и аргументы

Параметр - переменная в объявлении метода (шаблон).
Аргумент - конкретное значение при вызове.

// Параметры: name и age - это параметры
void Greet(string name, int age)
{
    Console.WriteLine($"{name}, вам {age} лет");
}

// Аргументы: "Алексей" и 25 - это аргументы
Greet("Алексей", 25);
Greet("Мария", 30);

// Именованные аргументы (порядок не важен)
Greet(age: 25, name: "Алексей");

6. Перегрузка методов (Overloading)

Несколько методов с одинаковым именем, но разными параметрами. Компилятор выбирает нужный вариант по типу и количеству аргументов.

// Перегруженные методы Print
void Print(int value)
{
    Console.WriteLine($"int: {value}");
}

void Print(string value)
{
    Console.WriteLine($"string: {value}");
}

void Print(int a, int b)
{
    Console.WriteLine($"sum: {a + b}");
}

// Компилятор выбирает нужный вариант автоматически
Print(42);          // вызовет Print(int)
Print("Hello");     // вызовет Print(string)
Print(3, 4);        // вызовет Print(int, int)
Перегрузка в Unity

В Unity часто встречается перегрузка: Instantiate(prefab) и Instantiate(prefab, position, rotation) - это разные перегрузки одного метода.


7. Параметры по умолчанию

Параметр с дефолтным значением необязателен при вызове.

void ShowMessage(string text, bool showBorder = false, int repeat = 1)
{
    for (int i = 0; i < repeat; i++)
    {
        if (showBorder) Console.WriteLine("---");
        Console.WriteLine(text);
    }
}

// Все три варианта корректны:
ShowMessage("Привет");                    // showBorder=false, repeat=1
ShowMessage("Привет", true);             // repeat=1
ShowMessage("Привет", true, 3);          // все явно

// Именованные параметры позволяют пропускать
ShowMessage("Привет", repeat: 3);        // showBorder остаётся false
Параметры по умолчанию - только в конце

Параметры со значением по умолчанию должны стоять после обязательных параметров.

void Bad(int a = 0, int b)  {} // ОШИБКА
void Good(int b, int a = 0) {} // Норма

Для углублённого изучения

Модификаторы параметров: ref, out, params

По умолчанию значимые типы передаются по значению (копия). Модификаторы меняют это поведение.

ref - передача по ссылке (чтение и запись)

void Double(ref int value)
{
    value *= 2;  // изменяем оригинал
}

int x = 5;
Double(ref x);
Console.WriteLine(x);  // 10 - оригинал изменился!

out - параметр только для вывода

// out: метод обязан присвоить значение параметру
bool TryParse(string input, out int result)
{
    if (int.TryParse(input, out result))
        return true;
    result = 0;  // обязательно присвоить
    return false;
}

if (TryParse("42", out int number))
    Console.WriteLine(number);  // 42
TryParse - паттерн с out

int.TryParse(), double.TryParse() - стандартные методы с out. Это безопасный способ конвертировать строку в число без исключения.

params - переменное число аргументов

int Sum(params int[] numbers)
{
    int total = 0;
    foreach (int n in numbers)
        total += n;
    return total;
}

Console.WriteLine(Sum(1, 2, 3));         // 6
Console.WriteLine(Sum(10, 20));          // 30
Console.WriteLine(Sum(1, 2, 3, 4, 5)); // 15

Стек вызовов и рекурсия

Стек вызовов (Call Stack)

При каждом вызове метода в памяти создаётся стековый фрейм - блок с локальными переменными и адресом возврата. Методы выполняются по принципу стека (LIFO).

Main() вызывает A()
  A() вызывает B()
    B() вызывает C()
    C() завершается → фрейм C удаляется
  B() завершается → фрейм B удаляется
A() завершается → фрейм A удаляется
Main() продолжает работу

Рекурсия

Метод вызывает сам себя. Обязательно должно быть базовое условие (выход из рекурсии).

// Рекурсивное вычисление факториала
int Factorial(int n)
{
    if (n <= 1) return 1;      // базовый случай - выход из рекурсии
    return n * Factorial(n - 1); // рекурсивный вызов
}

Console.WriteLine(Factorial(5)); // 5 * 4 * 3 * 2 * 1 = 120

StackOverflowException

Если рекурсия не завершается (нет базового условия или оно никогда не выполнится) - стек переполняется и программа аварийно завершается.

// ОПАСНО: бесконечная рекурсия
int Infinite(int n)
{
    return Infinite(n + 1); // нет условия выхода → StackOverflowException
}

Итог урока


📋 Перейти к практическим заданиям урока 4

Материалы для дополнительного ознакомления :

Методы
Параметры методов
Оператор return
Модификаторы параметров out, ref, in
Массив параметров
Рекурсия
Локальные функции
Перегрузка методов