Теперь рассмотрим более изощренный пример, когда внутри структуры у нас может быть переменная ссылочного типа, например, какого-нибудь класса:
class Program
{
private static void Main(string[] args)
{
State state1 = new State();
State state2 = new State();
state2.country = new Country();
state2.country.x = 5;
state1 = state2;
state2.country.x = 8; // теперь и state1.country.x=8, так как state1.country и state2.country
// указывают на один объект в хипе
Console.WriteLine(state1.country.x); // 8
Console.WriteLine(state2.country.x); // 8
Console.Read();
}
}
struct State
{
public int x;
public int y;
public Country country;
}
class Country
{
public int x;
public int y;
}
Переменные ссылочных типов в структурах также сохраняют в стеке ссылку на объект в хипе. И при присвоении двух структур state1 = state2; структура state1 также получит ссылку на объект country в хипе. Поэтому изменение state2.country повлечет за собой также изменение state1.country.
Объекты классов как параметры методов
Организацию объектов в памяти следует учитывать при передаче параметров по значению и по ссылке. Если параметры методов представляют объекты классов, то использование параметров имеет некоторые особенности. Например, создадим метод, который в качестве параметра принимает объект Person:
class Program
{
static void Main(string[] args)
{
Person p = new Person { name = "Tom", age=23 };
ChangePerson(p);
Console.WriteLine(p.name); // Alice
Console.WriteLine(p.age); // 23
Console.Read();
}
static void ChangePerson(Person person)
{
// сработает
person.name = "Alice";
// сработает только в рамках данного метода
person = new Person { name = "Bill", age = 45 };
Console.WriteLine(person.name); // Bill
}
}
class Person
{
public string name;
public int age;
}
При передаче объекта класса по значению в метод передается копия ссылки на объект. Это копия указывает на тот же объект, что и исходная ссылка, потому мы можем изменить отдельные поля и свойства объекта, но не можем изменить сам объект. Поэтому в примере выше сработает только строка person.name = "Alice".
А другая строка person = new Person { name = "Bill", age = 45 } создаст новый объект в памяти, и person теперь будет указывать на новый объект в памяти. Даже если после этого мы его изменим, то это никак не повлияет на ссылку p в методе Main, поскольку ссылка p все еще указывает на старый объект в памяти.
Но при передаче параметра по ссылке (с помощью ключевого слова ref) в метод в качестве аргумента передается сама ссылка на объект в памяти. Поэтому можно изменить как поля и свойства объекта, так и сам объект:
class Program
{
static void Main(string[] args)
{
Person p = new Person { name = "Tom", age=23 };
ChangePerson(ref p);
Console.WriteLine(p.name); // Bill
Console.WriteLine(p.age); // 45
Console.Read();
}
static void ChangePerson(ref Person person)
{
// сработает
person.name = "Alice";
// сработает
person = new Person { name = "Bill", age = 45 };
}
}
class Person
{
public string name;
public int age;
}
Операция new создаст новый объект в памяти, и теперь ссылка person (она же ссылка p из метода Main) будет указывать уже на новый объект в памяти.