Вопрос или проблема
Итак, я думал, что понимаю, как работают переменные значений и ссылок в C#, но столкнулся с случаем, который неясен и неинтуитивен для меня. Если у меня есть экземпляр класса типа “Assembly” (System.Reflection.Assembly) и я присваиваю его другой переменной, то, похоже, происходит “копирование”, а не передача по ссылке:
var assembly2= assembly1;
assembly1= LoadOtherAssembly();
Console.WriteLine(assembly2);
Console.WriteLine(assembly1);
Очевидно, что на второй строке (assembly1) я получаю другой сборку (что бы ни возвращал LoadOtherAssembly()), но “assembly2” кажется, что содержит оригинальную сборку.
Когда я делаю это с любым другим классом, этого не происходит (как и ожидалось):
public class Person
{
public string Name;
}
var dave=new Person();
dave.Name="Dave";
var culley=dave;
culley.Name="Culley";
Console.WriteLine(dave.Name);
Console.WriteLine(culley.Name);
В этом случае оба выводят “Culley”, как и ожидалось. Что я упустил, и чем это отличается от первого случая с использованием “Assembly”?
Да, это ошибка в размышлениях, как отмечено в комментариях. Каждый раз, когда я вызываю LoadOtherAssembly(), создается новый объект, и теперь обе переменные указывают на разные “адреса”/”объекты”.
Ответ или решение
В C# важно понимать различие между ссылочными и значимыми типами. Ссылочные типы, такие как классы, хранят ссылки на объекты в памяти, тогда как значимые типы хранят самих объектов. В вашем примере классы Assembly
и Person
являются ссылочными типами, однако важно рассматривать, что именно происходит при изменении переменной, указывающей на объект.
Когда вы пишете:
var assembly2 = assembly1;
assembly1 = LoadOtherAssembly();
На самом деле вы не "копируете" объект как таковой. Ваша переменная assembly2
получает ссылку на тот же объект, на который указывает assembly1
в момент присвоения. Однако при присваивании assembly1
новой сборки, assembly1
начинает указывать на другой объект, в то время как assembly2
по-прежнему указывает на исходный объект. Это и объясняет, почему assembly2
продолжает содержать ссылку на первоначальный объект сборки.
В случае с классом Person
, когда вы выполняете:
var culley = dave;
culley.Name = "Culley";
и затем обращаетесь к dave.Name
, вы видите измененное имя, потому что как dave
, так и culley
ссылаются на один и тот же объект, и, изменяя поле Name
через одну ссылку (culley
), вы изменяете объект, на который указывают обе переменные.
Таким образом, ключевое различие заключается в том, что в примере с Assembly
, когда вы присваиваете assembly1
другую сборку, вы меняете само значение assembly1
, а не сам объект, на который ссылается assembly2
. В случае с Person
обе переменные по-прежнему указывают на один и тот же объект в памяти, и изменение через одну из ссылок изменяет объект для обеих ссылок.
Если бы вы намеренно создавали новый объект Person
и присваивали его, тогда вы бы также увидели, что изменения в одной переменной не влияют на другую:
var dave = new Person();
dave.Name = "Dave";
var culley = new Person(); // Создаем новый объект
culley.Name = "Culley";
Console.WriteLine(dave.Name); // Выведет "Dave"
Console.WriteLine(culley.Name); // Выведет "Culley"
Подводя итог, можно сказать, что в случаях, когда вы видите, что переменные "не копируются", скорее всего, это связано с изменением ссылки на объект, а не с самим объектом.