Передача по значению
У нашего копировального аппарата есть ещё одна особенность. Он никогда не трогает ваши листы: прежде чем начать, он снимает с них точную копию и работает с ней. Что бы он ни делал с копией, ваши листы остаются на стекле нетронутыми.
Метод в C# ведёт себя так же. Посмотрим, что происходит, когда метод меняет свой параметр:
int x = 5;
AddTen(x); // 15
Console.WriteLine(x); // 5
void AddTen(int n)
{
n = n + 10;
Console.WriteLine(n);
}Копия при каждом вызове
Давайте разберём пример по строкам:
int x = 5;создаёт переменнуюx.- При вызове
AddTen(x)C# копирует значениеxв параметрn: теперь вnлежит5. n = n + 10меняет копию:nстановится15, и метод печатает15.- Метод завершается, и
nисчезает.Console.WriteLine(x)печатает5: переменнуюxникто не трогал.
Метод получает копию значения аргумента, а не саму переменную. Такой способ называют передачей по значению, и в C# он действует по умолчанию.
Для типов int, double, bool и char копия полностью независима от оригинала. Это значимые типы: их переменная хранит само значение, и при вызове копируется именно оно.
Этот вопрос часто задают на собеседовании: «Как C# передаёт аргументы в метод?» Ответ состоит из двух частей:
- по умолчанию по значению: метод получает копию, и присваивание параметру не меняет переменную вызывающего кода;
- передать саму переменную тоже можно, но это нужно явно написать и в объявлении метода, и в вызове.
Параметр — отдельная переменная
Параметр — обычная переменная метода:
- её можно читать и менять, как любую другую;
- она создаётся при каждом вызове и исчезает, когда метод завершается;
- с переменной вызывающего кода она не связана.
Есть два случая, когда связь только кажется.
Выражение в аргументе
Аргументом может быть выражение, а не только переменная. При вызове AddTen(x + 1) C# сначала вычисляет 6 и копирует это число в n. Метод видит только число и не знает, из чего его получили.
Одинаковые имена
Одинаковые имена тоже ничего не связывают. Если назвать параметр x, как переменную снаружи, переменных всё равно будет две. Внутри метода имя x означает параметр, и присваивание ему не трогает внешнюю x.
Вернуть новое значение
Методы из этого урока связаны с вызывающим кодом двумя путями:
- аргументы на входе;
- результат на выходе.
Чтобы вызывающий код получил новое значение, метод возвращает его через return. Что сделать с результатом, решает вызывающий код:
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;:
PlusTen(x)возвращает15, иConsole.WriteLineпечатает это число.- Следующая строка печатает
5: метод вернул новое значение, ноxне изменил. x = PlusTen(x)записывает результат второго вызова обратно вx.- Последняя строка печатает
15: переменная изменилась только после присваивания.
Аппарат выдал готовый лист, а подшили его в свою папку вы сами.
На этом аналогия заканчивается. Настоящему аппарату на каждую копию нужны бумага и время, а C# копирует значение аргумента при каждом вызове, и для числа это мгновенная операция.
Частая ошибка
Метод возвращает новое значение, но результат вызова никуда не записали:
int x = 5;
PlusTen(x); // ✗
Console.WriteLine(x); // 5Программа компилируется и печатает 5. Вызов метода может стоять отдельной инструкцией, даже если метод возвращает значение, и тогда результат пропадает. Компилятор об этом не предупреждает, поэтому записывайте результат явно: x = PlusTen(x);.
Главное
- Метод получает копию значения аргумента: присваивание параметру не меняет переменную вызывающего кода.
- В C# аргументы по умолчанию передаются по значению.
- Новое значение метод возвращает через
return, а вызывающий код сам записывает его в переменную:x = PlusTen(x);.