Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:
public Person StandartClone
{
Person p = (Person)this.MemberwiseClone ;
return(p);
}
Теперь клиенты класса могут легко создавать поверхностные клоны. Вот пример:
public void TestStandartClone
{
Person mother = new Person("Петрова Анна");
Person daughter = new Person ("Петрова Ольга");
Person son = new Person("Петров Игорь");
mother[0] = daughter;
mother[1] = son;
Person mother_clone = mother.StandartClone;
Console.WriteLine("Дети матери: {0}",mother.Fam);
Console.WriteLine (mother[0].Fam);
Console.WriteLine (mother[1].Fam);
Console.WriteLine("Дети клона: {0}",mother_clone.Fam);
Console.WriteLine (mother_clone[0].Fam);
Console.WriteLine (mother_clone[1].Fam);
}
При
Рис. 19.5. Поверхностное клонирование
Если стандартное поверхностное клонирование нас не устраивает, то класс можно объявить наследником интерфейса ICloneabie и реализовать метод Clone — единственный метод этого интерфейса. В нем можно реализовать полное глубокое клонирование или подходящую для данного случая модификацию.
Давайте расширим наш класс, придав ему родительский интерфейс ICloneabie. Реализация метода clone будет отличаться от стандартной реализации тем, что к имени объекта — полю Fam — будет приписываться слово "clone". Вот как выглядит этот метод:
public object Clone
{
Person р = new Person(this.fam + "_clone");
//копирование полей
p.age = this.age; p.children = this.children;
p.count_children = this.count_children;
p.health = this.health; p.salary = this.salary;
p.status = this.status;
return (p);
}
Эта реализация является слегка модифицированной версией стандартного поверхностного клонирования. Я добавил несколько строчек в тестирующую процедуру для проверки работы этой версии клона:
Person mother_clone2 = (Person)mother.Clone;
Console.WriteLine("Дети клона_2: {0}",mother_clone2.Fam);
Console.WriteLine (mother_clone2[0].Fam);
Console.WriteLine (mother_clone2[1].Fam);
Все работает должным образом.
Сериализация объектов
При работе с программной системой зачастую возникает необходимость в сериализации объектов. Под сериализацией понимают процесс сохранения объектов в долговременной памяти (файлах) в период выполнения системы. Под десериализацией понимают обратный процесс — восстановление состояния объектов, хранимых в долговременной памяти. Механизмы сериализации C# и Framework.Net поддерживают два формата сохранения данных — в бинарном файле и XML-файле. В первом случае данные при сериализации преобразуются в бинарный поток символов, который при десериализации автоматически преобразуется в нужное состояние объектов. Другой возможный преобразователь (SOAP formatter) запоминает состояние объекта в формате xml.
Сериализация позволяет запомнить рубежные состояния системы объектов с возможностью последующего возвращения к этим состояниям. Она необходима, когда завершение сеанса работы не означает завершение вычислений. В этом случае очередной сеанс работы начинается с восстановления состояния, сохраненного в конце предыдущего сеанса работы. Альтернативой сериализации является работа с обычной файловой системой, с базами данных и другими хранилищами данных. Поскольку механизмы сериализации, предоставляемые языком С#, эффективно поддерживаются. Net Framework, то при необходимости сохранения данных значительно проще
и эффективнее пользоваться сериализацией, чем самому организовывать их хранение и восстановление.Еще одно важное применение сериализации — это обмен данными удаленных систем. При удаленном обмене данными предпочтительнее формат xml из-за открытого стандарта передачи данных в Интернете по soap-протоколу, из-за открытого стандарта на структуру xml-документов. Обмен становится достаточно простым даже для систем, построенных на разных платформах и в разных средах разработки.
Так же, как и клонирование, сериализация может быть поверхностной, когда сериализуется на одном шаге единственный объект, и глубокой, когда, начиная с корневого объекта, сериализуется совокупность объектов, связанных взаимными ссылками (граф объектов). Глубокую сериализацию, часто обязательную, самому организовать непросто, так как она требует, как правило, рекурсивного обхода структуры объектов.
Если класс объявить с атрибутом [Serializable], то в него встраивается стандартный механизм сериализации, поддерживающий, что крайне приятно, глубокую сериализацию. Если по каким-либо причинам стандартная сериализация нас не устраивает, то класс следует объявить наследником интерфейса ISseriaizabie, реализация методов которого позволит управлять процессом сериализации. Мы рассмотрим обе эти возможности.
Класс с атрибутом сериализации
Класс, объекты которого предполагается сериализовать стандартным образом, должен при объявлении сопровождаться атрибутом [Serializable]. Стандартная сериализация предполагает два способа сохранения объекта: в виде бинарного потока символов и в виде xml-документа. В бинарном потоке сохраняются все поля объекта, как открытые, так и закрытые. Процессом этим можно управлять, помечая некоторые поля класса атрибутом [Nonserialized] — эти поля сохраняться не будут:
[Serializable] public class Test
{
public string name;
[NonSerialized] int id; int age;
//другие поля и методы класса
}
В класс Test встроен стандартный механизм сериализации его объектов. При сериализации поля name и аде будут сохраняться, поле id — нет.
Для запуска механизма необходимо создать объект, называемый форматером и выполняющий сериализацию и десериализацию данных с подходящим их форматированием. Библиотека FCL предоставляет два класса форматеров. Бинарный форматер, направляющий данные в бинарный поток, принадлежит классу BinaryFormatter. Этот класс находится в пространстве имен библиотеки FCL:
System.Runtime.Serialization.Formatters.Binary
Давайте разберемся, как устроен этот класс. Он является наследником двух интерфейсов: iFormatter и IRemotingFormatter. Интерфейс IFormatter имеет два открытых метода: Serialize и Deserialize, позволяющих сохранять и восстанавливать всю совокупность связанных объектов с заданным объектом В качестве корня. Интерфейс IRemotingFormatter имеет те же открытые методы: Serialize и Deserialize, позволяющие выполнять глубокую сериализацию, но в режиме удаленного вызова. Поскольку сигнатуры одноименных методов интерфейсов отличаются, то конфликта имен при наследовании не происходит — В классе BinaryFormatter методы Serialize и Deserialize перегружены. Для удаленного вызова задается дополнительный параметр, что и позволяет различать, локально или удаленно выполняются процессы обмена данными.