Теперь, когда вы лучше понимаете базовые отличия между типами значений и ссылочными типами, давайте обратимся к более сложному примеру. Предположим, что имеется следующий ссылочный тип (класс), который поддерживает информационную строку (InfoString
), устанавливаемую с применением специального конструктора:
class ShapeInfo
{
public string InfoString;
public ShapeInfo(string info)
{
InfoString = info;
}
}
Далее представим, что переменная типа ShapeInfo
должна содержаться внутри типа значения по имени Rectangle
. Кроме того, в типе Rectangle
предусмотрен специальный конструктор, который позволяет вызывающему коду указывать значение для внутренней переменной-члена типа ShapeInfo
. Вот полное определение типа Rectangle
:
struct Rectangle
{
// Структура Rectangle содержит член ссылочного типа.
public ShapeInfo RectInfo;
public int RectTop, RectLeft, RectBottom, RectRight;
public Rectangle(string info, int top, int left, int bottom, int right)
{
RectInfo = new ShapeInfo(info);
RectTop = top; RectBottom = bottom;
RectLeft = left; RectRight = right;
}
public void Display
{
Console.WriteLine("String = {0}, Top = {1}, Bottom = {2}, " +
"Left = {3}, Right = {4}",
RectInfo.InfoString, RectTop, RectBottom, RectLeft, RectRight);
}
}
Здесь ссылочный тип содержится внутри типа значения. Возникает важный вопрос: что произойдет в результате присваивания одной переменной типа Rectangle
другой переменной того же типа? Учитывая то, что уже известно о типах значений, можно корректно предположить, что целочисленные данные (которые на самом деле являются структурой — System.Int32
)должны быть независимой сущностью для каждой переменной Rectangle
. Но что можно сказать о внутреннем ссылочном типе? Будет ли полностью скопировано
static void ValueTypeContainingRefType
{
// Создать первую переменную Rectangle.
Console.WriteLine("-> Creating r1");
Rectangle r1 = new Rectangle("First Rect", 10, 10, 50, 50);
// Присвоить новой переменной Rectangle переменную r1.
Console.WriteLine("-> Assigning r2 to r1");
Rectangle r2 = r1;
// Изменить некоторые значения в r2.
Console.WriteLine("-> Changing values of r2");
r2.RectInfo.InfoString = "This is new info!";
r2.RectBottom = 4444;
// Вывести значения из обеих переменных Rectangle.
r1.Display;
r2.Display;
}
Вывод будет таким:
-> Creating r1
-> Assigning r2 to r1
-> Changing values of r2
String = This is new info!, Top = 10, Bottom = 50, Left = 10, Right = 50
String = This is new info!, Top = 10, Bottom = 4444, Left = 10, Right = 50
Как видите, в случае модификации значения информационной строки с использованием ссылки r2
для ссылки r1
отображается то же самое значение. По умолчанию, если тип значения содержит другие ссылочные типы, то присваивание приводит к копированию ссылок. В результате получаются две независимые структуры, каждая из которых содержит ссылку, указывающую на один и тот же объект в памяти (т.е. создается поверхностная копия). Для выполнения глубокого копирования, при котором в новый объект полностью копируется состояние внутренних ссылок, можно реализовать интерфейс ICloneable
(что будет показано в главе 8).
Передача ссылочных типов по значению
Бьерн Страуструп , Бьёрн Страуструп , Валерий Федорович Альмухаметов , Ирина Сергеевна Козлова
Программирование, программы, базы данных / Базы данных / Программирование / Учебная и научная литература / Образование и наука / Книги по IT