Типы, характеризуемые значениями и содержащие ссылочные типы
Типы, характеризуемые значениями и содержащие ссылочные типы
Теперь, когда вы чувствуете разницу между типами, характеризуемыми значением, и ссылочными типами, давайте рассмотрим более сложный пример. Предположим, что имеется следующий ссылочный тип (класс), обрабатывающий информационную строку, которую можно установить с помощью пользовательского конструктора.
class ShapeInfo {
public string infoString;
public ShapeInfo(string info) { infoString = info; }
}
Предположим также, что вы хотите поместить переменную этого типа класса в тип с именем MyReсtangle (прямоугольник), характеризуемый значением. Чтобы позволить внешним объектам устанавливать значение внутреннего поля ShapeInfо, вы должны создать новый конструктор (при этом конструктор структуры, заданный по умолчанию, является зарезервированным и не допускает переопределения).
struct MyRectangle {
// Структура MyRectangle содержит член ссылочного типа.
public ShapeInfo reсtInfo;
public int top, left, bottom, right;
public MyRactangle(string info) {
rectInfo = new ShapeInfo(info);
top = left = 10;
bottom = right = 100;
}
}
Теперь вы имеете ссылочный тип. внутри типа, характеризуемого значением. И здесь возникает вопрос на миллион долларов: что случится, если присвоить одну переменную типа MyRectangle другой такой же переменной? С учетом того, что вы уже знаете о типах, характеризуемых значениями, вы можете сделать правильное предположение о том, что целые данные (которые на самом деле и формируют эту структуру) для каждой переменной MyRectangle должны быть независимыми элементами. Но что можно сказать о внутреннем ссылочном типе? Будет скопировано полное состояние этого объекта или будет скопирована ссылка на этот объект? Проанализируйте следующий программный код и рассмотрите рис. 3.14, который может подсказать правильный ответ.
static void Main(string[] args) {
// Создание первого объекта MyRectangle.
Console.WriteLine("-› Создание r1");
MyRectangle r1 = new MyRectangle("Это мой первый прямоугольник");
// Присваивание новому MyRectangle значений r1.
Console.WriteLine("-› Присваивание r1 типу r2");
MyRectangle r2;
r2 = r1;
// Изменение значений r2.
Console.WriteLine("-› Изменение значений r2");
r2.rectInfo.InfoString = "Это новая информация!");
r2.bottom = 4444;
// Print values
Console.WriteLine("-› Значения после изменений:");
Console.WriteLine("-› r1.rectInfo.infoString: {0}", r1.rectInfo.infoString);
Console.WriteLine("-› r2.rectInfo.infoString: {0}", r2.rectInfo.infoString);
Console.WriteLine("-› r1.bottom: {0}", r1.bottom);
Console.WriteLine("-› r2.bottom: {0}", r2.bottom);
}
Рис. 3.14. Внутренние ссылки указывают на один и тот же объект
Как видите, при изменении значения информирующей строки с помощью ссылки r2 ссылка r1 отображает точно такое же значение. По умолчанию, когда тип, характеризуемый значением, содержит ссылочные типы, присваивание приводит к копированию ссылок. В результате вы получаете две независимые структуры, каждая из которых содержит ссылки, указывающие на один и тот же объект в памяти (т.е. "поверхностную копию"). Если вы хотите иметь "детальную копию", когда состояние внутренних ссылок полностью Копируется в новый объект, необходимо реализовать интерфейс ICloneable (это будет обсуждаться в главе 7).
Исходный код. Проект ValAndRef размещен в подкаталоге, соответствующем главе 3.