// Вспомните, что int - на самом деле структура System.Int32.
int i = 0;
// Вспомните, что Point - в действительности тип структуры.
Point p = new Point;
} // Здесь i и р покидают стек!
Использование типов значений ссылочных типов и операции присваивания
Когда переменная одного типа значения присваивается переменной другого типа значения, выполняется почленное копирование полей данных. В случае простого типа данных, такого как System.Int32
, единственным копируемым членом будет числовое значение. Однако для типа Point
в новую переменную структуры будут копироваться значения полей X
и Y
. В целях демонстрации создайте новый проект консольного приложения по имени FunWithValueAndReferenceTypes
и скопируйте предыдущее определение Point
в новое пространство имен, после чего добавьте к операторам верхнего уровня следующую локальную функцию:
// Присваивание двух внутренних типов значений дает
// в результате две независимые переменные в стеке.
static void ValueTypeAssignment
{
Console.WriteLine("Assigning value types\n");
Point p1 = new Point(10, 10);
Point p2 = p1;
// Вывести значения обеих переменных Point.
p1.Display;
p2.Display;
// Изменить pl.X и снова вывести значения переменных.
// Значение р2.Х не изменилось.
p1.X = 100;
Console.WriteLine("\n=> Changed p1.X\n");
p1.Display;
p2.Display;
}
Здесь создается переменная типа Point(p1)
, которая присваивается другой переменной типа Point(р2)
. Поскольку Point
— тип значения, в стеке находятся две копии Point
, каждой из которых можно манипулировать независимым образом. Поэтому при изменении значения p1.X
значение р2.X
остается незатронутым:
Assigning value types
X = 10, Y = 10
X = 10, Y = 10
=> Changed p1.X
X = 100, Y = 10
X = 10, Y = 10
По контрасту с типами значений, когда операция присваивания применяется к переменным ссылочных типов (т.е. экземплярам всех классов), происходит перенаправление на то, на что ссылочная переменная указывает в памяти. В целях иллюстрации создайте новый класс по имени PointRef
с теми же членами, что и у структуры Point
, но только переименуйте конструктор в соответствии с именем данного класса:
// Классы всегда являются ссылочными типами.
class PointRef
{
// Те же самые члены, что и в структуре Point...
// Не забудьте изменить имя конструктора на PointRef!
public PointRef(int xPos, int yPos)
{
X = xPos;
Y = yPos;
}
}
Задействуйте готовый тип PointRef
в следующем новом методе. Обратите внимание, что помимо использования класса PointRef
вместо структуры Point
код идентичен коду метода ValueTypeAssignment
:
static void ReferenceTypeAssignment
{
Console.WriteLine("Assigning reference types\n");
PointRef p1 = new PointRef(10, 10);
PointRef p2 = p1;
// Вывести значения обеих переменных PointRef.
p1.Display;
p2.Display;
// Изменить pl.X и снова вывести значения.
p1.X = 100;
Console.WriteLine("\n=> Changed p1.X\n");
p1.Display;
p2.Display;
}
В рассматриваемом случае есть две ссылки, указывающие на тот же самый объект в управляемой куче. Таким образом, когда значение X
изменяется с использованием ссылки p1
, изменится также и значение р2.X
. Вот вывод, получаемый в результате вызова этого нового метода:
Assigning reference types
X = 10, Y = 10
X = 10, Y = 10
=> Changed p1.X
X = 100, Y = 10
X = 100, Y = 10
Использование типов значений, содержащих ссылочные типы
Бьерн Страуструп , Бьёрн Страуструп , Валерий Федорович Альмухаметов , Ирина Сергеевна Козлова
Программирование, программы, базы данных / Базы данных / Программирование / Учебная и научная литература / Образование и наука / Книги по IT