Передача ссылочного параметра в метод по значению

105
07 марта 2022, 12:10

В документации microsoft написано, что если передавать ссылочный тип по значению, то фактически в метод передаётся копия на ссылку. Тогда почему если в методе изменить этот параметр, то потом вернувшись в main значение остаётся изменённым? Если бы передавалась сама ссылка а не её копия тогда было бы понятно.

Answer 1

Можно рассмотреть такой пример

class MyClass
{
    // инициализируем поле 20.
    public int Val = 20;
}
class Program
{
    static void MyMethod(MyClass f1, int f2)
    {
        // Увеличиваем на 5 значение поля у ссылочного типа
        f1.Val = f1.Val + 5;
        // Увеличиваем на 5 значение у значимого типа
        f2 = f2 + 5; 
        Console.WriteLine($"Окончание метода MyMethod(): f1.Val=={f1.Val}, f2=={f2}");
    }
    static void Main(string[] args)
    {
        //создаем ссылочный тип
        MyClass a1 = new MyClass();
        //создаем значимый тип
        int a2 = 10;
        Console.WriteLine($"До вызова метода MyMethod(): a1.Val=={a1.Val}, a2=={a2}");
        //вызоваем метод
        MyMethod(a1, a2);
        Console.WriteLine($"После отработки метода MyMethod(): a1.Val=={a1.Val}, a2=={a2}");
        Console.ReadLine();
    }
}

Результат работы такой

Что происходит при этом в Стеке и Куче поможет понять такая иллюстрация

Пример и иллюстрация взяты из книги Illustrated C# 7

P.S. для случая передачи параметров по ссылке изменим в предыдущем примере метод так

static void MyMethod(ref MyClass f1, ref int f2)

и его вызов так

MyMethod(ref a1, ref a2);

Вывод будет таким Иллюстрация к происходящему

Теперь изменим пример так

class Program
{
    static void MyMethod(MyClass f1)
    {
        f1.Val = 50;
        Console.WriteLine($"После изменения значения в MyMethod(): { f1.Val }");
        //здесь мы изменяем ссылку на др. экземпляр класса
        f1 = new MyClass();
        Console.WriteLine($"После присвоения нового экземпляра класса в MyMethod(): {f1.Val}");
    }
    static void Main(string[] args)
    {
        MyClass a1 = new MyClass();
        Console.WriteLine($"До вызова метода MyMethod():{a1.Val}");
        MyMethod(a1);
        Console.WriteLine($"После отработки метода MyMethod(): {a1.Val}");
        Console.ReadLine();
    }
}

Результат работы такой Иллюстрация происходящего

Теперь снова изменим метод

static void MyMethod(ref MyClass f1)

и его вызов

MyMethod(ref a1);

Результат работы такой Иллюстрация к присходящему Тут уж все возможные случаи продемонстрированы.

READ ALSO
Sfml c# linux как компилить?

Sfml c# linux как компилить?

Как работать с sfml на с# в линуксЕсть идея с моно, но есть желание разрабатывать сразу на ubuntu и запускать

164
Многопоточность в win-службе на .net core

Многопоточность в win-службе на .net core

Помогите разобраться с правильным использованием многопоточности в виндовс-службе, написанной наnet core

88
Не закрывать базовый поток

Не закрывать базовый поток

Можно ли как-то сделать так, что бы не вызывался Dispose при использовании оберток StreamReader / StreamWriter?

86