Давайте сразу начнем с кода. У нас есть вот такой код, давайте разберемся почему он выводит то, что выводит:
class Program
{
static void Main(string[] args)
{
int a = 1;
ProcessValue(a);
Console.WriteLine(a.ToString());
//здесь будет выведено 1
TestClass test = new TestClass();
test.val = 2;
ProcessRef(test);
Console.WriteLine(test.val.ToString());
//здесь будет выведено 10
TestClass test = new TestClass();
test.val = 3;
Process(test);
Console.WriteLine(test.val.ToString());
//а здесь будет выведено 3
}
static void ProcessValue(int par)
{
par = 3;
}
static void ProcessRef(TestClass par)
{
par.val = 10;
}
static void Process(TestClass par)
{
par = new TestClass();
par.val = 100;
}
}
public class TestClass
{
public int val;
}
Вы, наверняка, знаете, что в .Net есть значимые (value) и ссылочные (reference) типы. Первые - размещаются полностью в стеке, а вторые размещаются в управляемой куче, а в стеке размещается только указатель на них.
Второе полезное знание, которое многие, к сожалению не всегда знают или понимают состоит в том, что все параметры по умолчанию передаются в методы по значению, то есть, проще говоря, копируются.
То есть, при передаче в качестве параметра объекта значимого типа, в стеке создается такой же точно объект, и при передаче объекта ссылочного типа происходит то же самое. Но, если в случае значимого типа, объект в стеке и так представляет собой значение, то в объекте ссылочного типа в стеке находится только ссылка на сам объект, соответственно внутри метода все изменения происходят в нем же. Вот иллюстрация процесса:
А почему же тогда в третьем случае результат будет 3, а не 100? Все по тому же, потому что значения передаются КОПИРОВАНИЕМ. То есть, в момент выполнения присваивания внутри функции Process стек у нас будет выглядеть примерно так.
Я немного не корректно нарисовал одинаковые стрелочки, так как при передаче параметра в функцию исходная ссылка копируется, а при создании объекта внутри функции изменяется сама ссылка, но суть происходящего отражена правильно - все действия внутри функции происходят с копией указателя на объект, а не с самим указателем. То есть, test как указывал на объект с val=3, так на него и указывает. Вот еще одна картинка - стек на момент выхода из метода Process()
А как же сделать, чтобы и в третьем случае послы выхода из Process() наш объект содержал новое значение? Сделать это очень просто, достаточно использовать в определении метода и при его вызове ключевое слово ref
class Program
{
static void Main(string[] args)
{
TestClass test = new TestClass();
test.val = 3;
Process(ref test);
Console.WriteLine(test.val.ToString());
//теперь результат - 100
}
static void Process(ref TestClass par)
{
par = new TestClass();
par.val = 100;
}
}
Теперь параметр в метод Process() будет передаваться по ссылке, то есть par будет представлять собой не копию test (то есть указателя на область памяти, где размещено сам объект), а указатель на этот указатель, таким образов все изменения производимые с параметром внутри метода будут происходить не с копией объекта, а с ним самим.



Комментариев нет:
Отправить комментарий