Перейти к содержимому
Теория · Методы · урок 4 из 8

Передача по значению

У нашего копировального аппарата есть ещё одна особенность. Он никогда не трогает ваши листы: прежде чем начать, он снимает с них точную копию и работает с ней. Что бы он ни делал с копией, ваши листы остаются на стекле нетронутыми.

Метод в C# ведёт себя так же. Посмотрим, что происходит, когда метод меняет свой параметр:

C#
int x = 5;
AddTen(x);            // 15
Console.WriteLine(x); // 5

void AddTen(int n)
{
    n = n + 10;
    Console.WriteLine(n);
}

Копия при каждом вызове

Давайте разберём пример по строкам:

  1. int x = 5; создаёт переменную x.
  2. При вызове AddTen(x) C# копирует значение x в параметр n: теперь в n лежит 5.
  3. n = n + 10 меняет копию: n становится 15, и метод печатает 15.
  4. Метод завершается, и n исчезает. Console.WriteLine(x) печатает 5: переменную x никто не трогал.

Метод получает копию значения аргумента, а не саму переменную. Такой способ называют передачей по значению, и в C# он действует по умолчанию.

Для типов int, double, bool и char копия полностью независима от оригинала. Это значимые типы: их переменная хранит само значение, и при вызове копируется именно оно.

Этот вопрос часто задают на собеседовании: «Как C# передаёт аргументы в метод?» Ответ состоит из двух частей:

  • по умолчанию по значению: метод получает копию, и присваивание параметру не меняет переменную вызывающего кода;
  • передать саму переменную тоже можно, но это нужно явно написать и в объявлении метода, и в вызове.

Параметр — отдельная переменная

Параметр — обычная переменная метода:

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

Есть два случая, когда связь только кажется.

Выражение в аргументе

Аргументом может быть выражение, а не только переменная. При вызове AddTen(x + 1) C# сначала вычисляет 6 и копирует это число в n. Метод видит только число и не знает, из чего его получили.

Одинаковые имена

Одинаковые имена тоже ничего не связывают. Если назвать параметр x, как переменную снаружи, переменных всё равно будет две. Внутри метода имя x означает параметр, и присваивание ему не трогает внешнюю x.

Вернуть новое значение

Методы из этого урока связаны с вызывающим кодом двумя путями:

  • аргументы на входе;
  • результат на выходе.

Чтобы вызывающий код получил новое значение, метод возвращает его через return. Что сделать с результатом, решает вызывающий код:

C#
int x = 5;
Console.WriteLine(PlusTen(x)); // 15
Console.WriteLine(x);          // 5
x = PlusTen(x);
Console.WriteLine(x);          // 15

int PlusTen(int n)
{
    return n + 10;
}

Разберём строки после int x = 5;:

  1. PlusTen(x) возвращает 15, и Console.WriteLine печатает это число.
  2. Следующая строка печатает 5: метод вернул новое значение, но x не изменил.
  3. x = PlusTen(x) записывает результат второго вызова обратно в x.
  4. Последняя строка печатает 15: переменная изменилась только после присваивания.

Аппарат выдал готовый лист, а подшили его в свою папку вы сами.

На этом аналогия заканчивается. Настоящему аппарату на каждую копию нужны бумага и время, а C# копирует значение аргумента при каждом вызове, и для числа это мгновенная операция.

Частая ошибка

Метод возвращает новое значение, но результат вызова никуда не записали:

C#
int x = 5;
PlusTen(x); // ✗
Console.WriteLine(x); // 5

Программа компилируется и печатает 5. Вызов метода может стоять отдельной инструкцией, даже если метод возвращает значение, и тогда результат пропадает. Компилятор об этом не предупреждает, поэтому записывайте результат явно: x = PlusTen(x);.

Главное

  • Метод получает копию значения аргумента: присваивание параметру не меняет переменную вызывающего кода.
  • В C# аргументы по умолчанию передаются по значению.
  • Новое значение метод возвращает через return, а вызывающий код сам записывает его в переменную: x = PlusTen(x);.