Почему экземпляр, переданный в метод с ключевым словом ref, может оставаться неизменным после создания нового объекта?
Я столкнулся с этой сложной проблемой. Смотрите следующий код:
public class Foo
public int A { get; set; }
}
static void Main(string[] args)
{
Foo foo1 = new Foo();
Foo foo2 = new Foo();
ChangeObject1(foo1);
ChangeObject2(ref foo2);
Console.WriteLine("Foo1 = " + foo1.A);
Console.WriteLine("Foo2 = " + foo2.A);
Console.ReadKey();
}
static void ChangeObject1(Foo foo)
{
foo.A += 1;
foo = new Foo();
}
static void ChangeObject2(ref Foo foo)
{
foo.A += 1;
foo = new Foo();
}
После некоторого тестирования foo1.A равно 1, а foo2.A по-прежнему равно 0. Насколько я понимаю, это связано с тем, что во втором методе экземпляр передается по ссылке, а не по значению. Это изменяет содержимое экземпляра, поэтому, когда новый объект создается и присваивается в методе, значение больше не определяется.
Но размышления об этом смущают меня, так как я чувствую, что в первом методе должны быть аналогичные эффекты, даже если он передается по значению, поскольку содержимое экземпляра все еще изменяется вне метода.
Кто-нибудь знает, что на самом деле происходит? Спасибо!
2 ответа
Я предполагаю, что вы используете C# или Java. в любом случае, концепция остается прежней
когда вы объявляете свои переменные foo
Foo foo1 = new Foo()
вы объявляете так называемый ссылочный тип. (все объекты, которые представляют классы, являются ссылочными типами)
Итак, значение хранится в foo1
на самом деле не Foo
, но это значение, которое содержит адрес памяти этого Foo
,
когда ты звонишь
static void ChangeObject1(Foo foo)
{
foo.A += 1;
foo = new Foo();
}
Вы передаете копию этой ссылки. Поэтому, когда вы делаете foo.A += 1
, вы меняете значение члена объекта, который вы только что передали.
когда вы делаете foo = new Foo();
вы изменяете только копию ссылки, но не саму исходную ссылку на объект.
теперь, когда вы звоните
static void ChangeObject2(ref Foo foo)
{
foo.A += 1;
foo = new Foo();
}
вы не передаете копию адреса вашего Foo
но вы передаете адрес адреса вашего Foo
,
Вы можете думать об этом, как будто вы передаете оригинал Foo
сам адрес
Поэтому, когда вы звоните foo = new Foo()
, не имеет значения, что вы сделали с Foo до этого. Вы только что переписали свой адрес с новым адресом Foo
объект.
если вы измените свой метод, чтобы выглядеть следующим образом
static void ChangeObject2(ref Foo foo)
{
foo.A += 1;
}
тогда вы должны заметить изменение
Вот страница, которая объясняет ссылочные типы. Это на языке Java, но те же принципы применимы и к C#.
Возможно, если вы думаете об этом с точки зрения типа значения вместо ссылочного типа, это поможет. Возьмем первый пример (число в скобках представляет адрес памяти, который содержит целочисленное значение).
Передача по значению
[0001] -> 5;
Когда вы передаете по значению, значение 5 копируется в новый адрес памяти
[0002] -> 5;
[0002] -> 10; // changes the value of a new reference
Изменение значения [0002] не повлияет на [0001].
Передача по ссылке
[0001] -> 5;
С другой стороны, если вы передадите сам адрес (передавая ключевое слово ref)
[0001] -> 5;
[0001] -> 10; // changes the value of the original reference
Теперь вы можете изменить значение, которое содержит ссылка.
Типы ссылок вместо целого числа содержат адрес для объекта. Таким образом, это означает, что если вы передаете по ссылке, вы передаете "контейнер" (т. Е. Указатель / ссылку), который содержит значение. Затем вы можете изменить содержимое контейнера. Если вы передаете по значению, вы берете значение из контейнера и передаете его, тогда исходный контейнер исчезает из картинки.