День сто пятьдесят шестой. #ЗаметкиНаПолях

Сериализация. Окончание

Пример использования для глубокого копирования

При создании копии объекта возникает проблема с тем, что создаётся поверхностная копия. Даже метод MemberwiseClone в System.Object создает поверхностную копию нового объекта. Копирование объекта выполняется свойство за свойством, если свойство является значимым типом, то копируются данные по битам, а если свойство является ссылочным типом, то копируется ссылка на исходный объект. Другими словами, поле или свойство объекта-клона будет ссылаться на тот же объект.

Одним из способов решить эту проблему, то есть создать глубокую копию объекта, будет использование сериализации. Следующий класс создаёт метод-расширение для глубокого копирования объектов через сериализацию. Замечания:

- объекты копирования должны быть помечены атрибутом Serializable;

- мы используем упомянутый выше класс StreamingContext, задавая его свойству State значение StreamingContextStates.Clone (то есть мы сериализуем объект с целью его копирования).

public static class ObjectExtension

{

public static T DeepCopy<T>(this T objSource)

{

using (MemoryStream stream = new MemoryStream())

{

BinaryFormatter formatter = new BinaryFormatter();

formatter.Context = new StreamingContext(StreamingContextStates.Clone);

formatter.Serialize(stream, objSource);

stream.Position = 0;

return (T)formatter.Deserialize(stream);

}

}

}


Рассмотрим пример использования этого метода расширения. Допустим, у нас есть простой класс:

[Serializable]

private class Person

{

public string Name { get; set; }

}

В примере ниже мы составляем список объектов этого класса. Затем делаем поверхностную копию этого списка, а затем глубокую копию. Отличие в том, что, если изменить значение свойства одного из объектов, то изменение затронет как изначальный список, так и его поверхностную копию. А вот глубокая копия останется неизменной:

var jeff = new Person { Name = "Jeff" };

var kris = new Person { Name = "Kristin" };



// изначальный список

var persons = new List<Person> { jeff, kris };

// поверхностная копия

var shallowCopy = new List<Person>(persons);

// глубокая копия

var deepCopy = objects.DeepCopy();

// изменяем свойство объекта

jeff.Name = "Bob";



Если вывести в консоль значения свойств Name объектов из каждого списка, мы получим:

persons: Bob, Kristin

shallowCopy: Bob, Kristin

deepCopy: Jeff, Kristin



Источник: Джеффри Рихтер “CLR via C#”. 3-е изд. – СПб.: Питер, 2012. Глава 24.