Показать сообщение отдельно
Старый 08.06.2010, 23:16   #1
Dream
быдло
 
Регистрация: 05.08.2007
Сообщений: 1,435
Написано 614 полезных сообщений
(для 1,489 пользователей)
Радость Полу урок: Сериализация.

По просьбе автора две части статьи были соединены в одну тему.
Все комментарии были перемещены в отдельную тему. Просьба комментарии теперь писать только туда, чтобы не засорять этот топик!
- АВТОМАТ


Сериализация обьектов
.Вступление
Думаю много кто из вас делал загрузку/сохранение пареметров программы/игры, и часто сталкивался с разными проблемами связаными с этим делом.
Хотелось поделится опытом, услышать от вас идеи по улучшению.
1. Ввод в сериализацию
Как гласит вика
"Сериализация (в программировании) — процесс перевода какой-либо структуры данных в последовательность битов. Обратной к операции сериализации является операция десериализации — восстановление начального состояния структуры данных из битовой последовательности."
То есть это сохранения наших классов, с их, состоянием в файл. C# даёт удобную(но далеко не идеальную) возможность для работы с сериализцией System.Xml.Serialization. Он записывает данные в файл в виде Xml структуры, что удобно, если нужно будет править файл вручную, и в общем-то читабельней.
Минусом использования этого класса является то, что на сериализацию больших классов многократно (дупустим некое подобие базы данных) будет тратится довольно таки ощутимое время, что не совсем хорошо.
Давайте попробуем обойти это
2.
Один из способов избежать повторной сериализации структуры - сохранять шаблон, и при следующем вызове сериализации использовать его.
Сейчас напишем базовый класс для нашого сериализатора.
public class XmlHelper
    
{
        
#region members
        #endregion

        #region Static
        
public static XmlSerializer GetSerializer(Type type)
        {
           return 
null;
        }

        public static 
void WriteEntity(object objstring fileFullPath)
        {
        }

        public static 
T GetEntity<T>(string fileFullPath)
        {
            return 
null;
            
        }
        
#endregion
    

Примечание: как видите в коде используется #region советую и вам его использовать, это облегчает навигацию по коду, а также даёт возможность свернуть группы( например методы, поля, свойства и т.д.) чтобы нельзя было спутать их с другими участками кода
Примечание 2:для сериализатора нужно будет использовать следующие библиотеки
#region using
using System;
using System.Collections;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml.Serialization;
#endregion 
Все методы объявлены как статик, потому что пока что нам не нужно создавать несколько экземпляров помощника, и можно будет обращаться из любого участка кода для сериализации нужного нам объекта.
Итак, давайте разберём по методом, что у нас есть. А есть у нас всего три метода, которые, пока что, как заглушки:
GetSerializer(Type type) - метод который будет возвращать сериализатор для указанного типа - "Type type"
WriteEntity(object obj, string fileFullPath) - метод который будет записывать в файл полученный объект (object obj) любого типа в файл (string fileFullPath). Путь к файлу желательно писать полный, во избежания недоразумений.
T GetEntity<T>(string fileFullPath) - метод который будет производить десериализацию указанного файла (string fileFullPath) в тип T, и возвратит нам экземпляр этого класса(T).
Давайте наполнять наш класс.
Во первых, создадим хранилище для сериализаторов, в таком деле я предпочитаю использовать коллекцию типа Hashtable (хотя тут дело каждого можно использовать и обычный IDictionary<string,XmlSerializer>)

private static readonly Hashtable hash = new Hashtable(); 
сделаем её приватной чтобы не было доступа не из нашего класса сериализатора (безопасный код, это важная вещь) и не было соблазна что-то засунуть туда.
Так как переменная у нас static, то можно сразу же её объявить, чтобы потом не переживать (хотя я рекомендую всё-таки делать конструкторы даже для статик классов).
Идем дальше. Заходим в метод GetSerializer
Обьявляем переменную для хранения возвращаемого сериализатора
XmlSerializer res
далее, так как приложение может быть многопоточное, нужно обезопасить себя, чтобы одновременно несколько обьектов не попытались получить сериализатор из нашей коллекции, так как это вызовет Ошибку.
и так лочим нашу коллекцию
lock (hash)
{


команда lock пытается получить к объекту (hash) монопольный доступ, и не начнет выполнение последующих за ней операторов, пока не получит этот самый доступ.
Внутри попытаемся получить сериализатор из нашей коллекции, в которой сохранены имена типов, который уже были сериализованы, и собственно сам сериализатор.
res hash[type.FullName] as XmlSerializer
так как в коллекции хранятся объекты в виде object, пытаемся привести его к XmlSerializer. если в колеции не будет этого типа, то переменная res будет равна null. идём дальше, проверим не равняется ли res null, и если равняется, прийдётся нам всётаки получить у системы сериализатор даного нудного класса.
if (res == null)
                {
                    
res = new XmlSerializer(type);
                    
hash[type.FullName] = res;
                } 
после получения сериализатора записываем его в коллекцию сериализаторов с ключём type.FullName - который соответствует полному имени класса, для которго мы создали сериализатор.
Вот собственно как должен после всего этого выглядеть метод

public static XmlSerializer GetSerializer(Type type)
        {
            
XmlSerializer res;
            
lock (hash)
            {
                
                
res hash[type.FullName] as XmlSerializer;
                if (
res == null)
                {
                    
res = new XmlSerializer(type);
                    
hash[type.FullName] = res;
                }
            }
            return 
res;
        } 
Теперь давайте заполним метод WriteEntity, который записывает класс в файл.
Для начала создаём сериализатор, с использованием только что написанного нами метода.
XmlSerializer serializer GetSerializer(obj.GetType()); 
Далее создадим StringBuilder, который помогает работать со строками и кодировками.
StringBuilder stringBuilder = new StringBuilder(); 
Также нужно создать класс-поток который производить работу со строками и передадим ему наш билдер строк.
StringWriter stringWriter = new StringWriter(stringBuilder CultureInfo.InvariantCulture); 
Начинаем сериализацию обьекта obj в поток:
serializer.Serialize(stringWriter obj); 
Закрываем поток:
stringWriter .Close(); 
Всё информация в виде строк сохранилась в наш билдер строк -stringBuilder, давайте получим всё в одну строку.
string xml stringBuilder .ToString(); 
Создадим экземпляр класса, который производить работу с записью информации в файл. Передадим в конструктор имя нужного нам файла который получили в параметрах метода:
StreamWriter sw = new StreamWriter(fileFullPath); 
Записываем:
sw.Write(xml); 
И не забываем закрыть поток.
sw.Close(); 
Результат в методе -

public static void WriteEntity(object objstring fileFullPath)
        {
            
XmlSerializer sr GetSerializer(obj.GetType());
            
StringBuilder sb = new StringBuilder();
            
StringWriter w = new StringWriter(sbCultureInfo.InvariantCulture);
            
sr.Serialize(wobj);
            
w.Close();
            
string xml sb.ToString();
            
StreamWriter sw = new StreamWriter(fileFullPath);
            
sw.Write(xml);
            
sw.Close();
        } 
Вот собственно и всё, мы сделали сериализацию объекта в файл.

Работать с этим классом так
XmlHelper.WriteEntity(MyClass,"С:\pron\list.extention"); 
Хочу заметить, что сериализуються ТОЛЬКО публичные свойства и поля класса(public) и называются они в файле также как вы назовёте их в коде.
Пока что всё.
Завтра напишу Про то как производить десериализацию из файла( а лучше не ждите и попробуйте сами сделать). а также про то, как назначить имена при сериализации и прочие нюансы. Удачи!
З.Ы. Спасибо ffinder за корректуру.

Последний раз редактировалось Dream, 11.06.2010 в 01:51.
(Offline)
 
Ответить с цитированием
Эти 14 пользователя(ей) сказали Спасибо Dream за это полезное сообщение:
ABTOMAT (08.06.2010), den (05.08.2010), Erkon (13.03.2013), Fatalix3d (08.06.2010), ffinder (09.06.2010), h1dd3n (09.06.2010), Harter (12.06.2010), IGR (08.06.2010), Lestar (18.06.2011), Nex (09.06.2010), pax (09.06.2010), Radnk (25.08.2012), Randomize (09.06.2010), SBJoker (08.06.2010)