Типы, характеризуемые значениями и содержащие ссылочные типы

Типы, характеризуемые значениями и содержащие ссылочные типы

Теперь, когда вы чувствуете разницу между типами, характеризуемыми значением, и ссылочными типами, давайте рассмотрим более сложный пример. Предположим, что имеется следующий ссылочный тип (класс), обрабатывающий информационную строку, которую можно установить с помощью пользовательского конструктора.

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.