С# для Windows. Сериализация объектов

К началу раздела

logo.gif

Глава 6 Сериализация объектов

В начало страницы

Параграф 1. О сериализации объектов

Сериализация - процесс преобразования некоторого объекта (класса) в последовательность байтов с целью сохранения в памяти (базе данных, файле), отправки объекта удаленному приложению посредством веб-службы, передача объекта из одного домена в другой, передача объекта через брандмауэр в виде XML-строки и хранение информации о безопасности или конкретном пользователе, используемой несколькими приложениями. Сериализация - процесс двунаправленный и имеет целью не только сохранить и передать в удобном виде данных, но и возможность воссоздать его при необходимости в виде исходного объекта. Обратный процесс называется десериализацией.

Пространство имен System.Runtime.Serialization содержит классы, необходимые для сериализации и десериализации объектов.

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

[Serializable]

Кроме того, для того, чтобы сериализация стала возможной необходимо также объявление пространств имен:

using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;

Дополнительно в объекте часть информации может требовать или не требовать сохранения и дальнейшего восстановления, в этом случае применяется метки-атрибуты [SerializableAttribute] и [NonSerializedAttribute].

Cериализуемая информация содержит не только данные, но и сведения о типе объекта (версию, язык и региональные параметры, а также имя сборки).

Основную информацию по выполнению сериализации проводит специальный объект - Formatter.

Как мы уже отмечали, различают двоичную и XML-сериализацию. При XML-сериализации информация сериализуется в XML-поток. XML-сериализация может также использоваться для сериализации объектов в потоки XML, которые соответствуют спецификации SOAP (Simple Object Access Protocol — простой протокол доступа к объектам). SOAP - это протокол, основанный на XML и созданный специально для передачи вызовов процедур с использованием XML.

Подробно мы будем говорить о XML-сериализации, когда руки автора доберутся до Web-сервисов. А пока познакомимся с двоичной сериализацией в файлы. И последнее замечание перед приведением примера использования сериализации, мы отмечали, что сериализованная информация хранит версию приложения и ряд других его атрибутов. Один из способов обойти это - настраиваемая сериализация, когда можно указать какие объекты будут сериализованы, и как будет производиться сериализация. Существует способ обхода этого варианта, связанные с сериализацией конструктора и некоторыми другими тонкостями, которые выходит за рамки статьи.

Впервые я использовал сериализацию при создании Русско-японского переводчика с целью хоть по минимуму защить труд автора словаря - вытащить из двоичного исходного файла Excel исходный документ словаря практически невозможно. Вот на этом примере я и покажу механизм двоичной сериализации.

В начало страницы

Параграф 2. Пример создания класса для двоичной сериализации

Итак, создадим пустое пока решение (например, с именем myProject) приложения и добавим к нему класс, который будет выполнять функции словаря. В окне "Solutation Explorer" (Меню Viev | Solutation Explorer) выполним правый клик мышкой на файле проекта и добавим новую опцию (Рис.1.).

mySer001.jpg

Рис.1 Создание приложения и добавление класса для сериализации

mySer002.jpg

Рис.2 Создание приложения и добавдение класса для сериализации

В код класса, который мы хотим сделать сериализуемым добавим помеченный как сериализуемый объект - [Serializable]:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace myProject
{
 [Serializable]
 class dictionary
 {
 }
}

В класс dictionary добавим методы, делающие класс словарем, например Hashtable, предоставляющую коллекцию пар ключ/значение, которые упорядочены по хэш-коду ключа и которая собственно будет хранить словарь. При добавлении элемента в коллекцию Hashtable он помещается в определенный сегмент в зависимости от хэш-кода ключа. В дальнейшем поиск ключа осуществляется только в определенном сегменте с использованием хэш-кода ключа. Таким образом, в значительной степени уменьшается количество операций сравнения ключей, которое требуется для нахождения элемента. Итак, первым добавим:

private Hashtable hashTable = new Hashtable();

Далее понадобятся такие методы, как число значений, проверки наличия слова в словаре, метод перевода слова, добавления и удаления записей в словарь:

public int Count
{
 get
 {
  return hashTable.Count;
 }
 }
 public bool Contains(string word)
 {
  return hashTable.Contains(word);
 }
 public string Translate(string word)
 {
  return (string)hashTable[word];
 }
 public void Add(string key, string value)
 {
  if(!hashTable.Contains(key))            
    hashTable.Add(key, value);
 }
 public void Remove(string key)
 {
  if(hashTable.Contains(key))            
    hashTable.Remove(key);
 }
 //Здесь можно добавить другие методы
 ..................................

}

В начало страницы

Параграф 3. Работа по сериализации и десериализации класса

Первым в основном приложении добавляем необходимые пространства имен, а именно:

using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;

И соответственно объявляем потомка созданного класса, и делаем из него рабочий класс - workDict=new workDict(); :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;

namespace myProject
{
 public partial class Form1 : Form
 {
  public dictionary workDict = null;
  public Form1()
  {
    InitializeComponent();
   }
   private void Form1_Load(object sender, EventArgs e)
   {
    workDict=new workDict();
   }

Далее можно пользоваться методами добавления и удаления данных в словарь, описанных выше.

Осталось сохранить словарь в сериализованном виде и загрузить вновь словарь в память.

Запись можно выполнить так (sCurDir - текущая директория старта приложения - там мы будем размещать и словарь):

private void vSave()
{
 string s = string.Empty;
 BinaryFormatter bf = null;
 string sDicPath = sCurDir + @"\Dictionary.dct";
 bool fYes = false;
 using (FileStream fs = File.Create(sDicPath))
 {
  try
  {
   bf = new BinaryFormatter();
   bf.Serialize(fs, workDict);
   fYes = true;
  }
  catch (SerializationException ex)
  {
    s = ex.Message;
    MessageBox.Show(s, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
  catch (IOException ex1) 
  {
   s = ex1.Message;
   MessageBox.Show(s, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
  }
  finally
  {
   if (fs != null)
        fs.Close();
    if (bf != null) bf = null;
  }
 }
 if (fYes)
 {
     s = "Сохранен словарь объемом " + Convert.ToString(workDict.Count) + 
                                                 "пар слов (словосочетаний, фраз).";
     MessageBox.Show(s, "Информация!", MessageBoxButtons.OK, MessageBoxIcon.Information);
 }
 else
 {
    s = "Словарь не сохранен из-за ошибки!";
    MessageBox.Show(s, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
 }
}

Для загрузки сериализованного файла словаря можно воспользоваться следующей функцией:

private void vDicLoad()
{
 string s = string.Empty;
 string s1 = string.Empty;
 bool fYes = false;
 BinaryFormatter bf = null;
 FileStream fs = null;
 try
 {
  if (workDict == null)
  {                  
    workDict = new Dictionary();
  }
  if (File.Exists(sCurDir + @"\Dictionary.dct"))
  {
    fs = new FileStream(sCurDir + @"\Dictionary.dct", FileMode.Open, FileAccess.Read);
    bf = new BinaryFormatter();
    workDict = bf.Deserialize(fs) as Dictionary;
    fYes = true;
   }                
     
  }
  catch (SerializationException ex)
  {
   label1.ForeColor = Color.Red;
   label1.Text = ex.Message;
  }
  catch (IOException ex1) 
  {
   label1.ForeColor = Color.Red;
   label1.Text = ex1.Message;
  }
  finally
  {
   if (fs != null)
       fs.Close();
   if (bf != null)
       bf = null;
  }
  if (fYes)
  {
   s = "Загружен словарь объемом " + Convert.ToString(workDict.Count) + " записей.";
   MessageBox.Show(s, "Информация!", MessageBoxButtons.OK, MessageBoxIcon.Information);    
  }
  else
  {

   s = "Словарь пока пуст. Вы можите приступить к его заполнению";
   MessageBox.Show(s, "Информация!", MessageBoxButtons.OK, MessageBoxIcon.Information);    
  }
}

Вот и все тонкости работы по двоичной сериализации и десиреализации, приведенные на кокретном примере работы со словарем.

Молчанов Владислав 1.05.2008г.

logo.gif

На главную страницу

В начало страницы

К началу раздела


Сайт управляется системой uCoz