Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Странности при десериализации

8.9K
22 мая 2011 года
Apach47
130 / / 14.06.2010
Здравствуйте уважаемые форумчане.
Прошу помочь мне разобраться со следующей проблемой, которая проявляется у меня при десериализации.

Начну с писания задачи.
Имею три класса - товар, менеджер товаров и главным менеджер, а так же пару форм. В товаре описано поле типа менеджера товаров, через которое происходит обращение к методу этого класса для назначения id товару.
Код:
private ProductManager _myProductManager;

        public Product(String name... **Констуктор с десятком параметров**)
        {
            //Сначала инициализируем менеджер
            _myProductManager = ProductManager.GetUniqProductManager();
            //Потом получаем последний элемент из списка товров, получаем его ID и прибавляем единицу
            _id = _myProductManager.GetMaximumID() + 1;
........................................
         }
.............................................................


Класс ProductManager реализует концепцию паттерна "Одиночка". Вот как я ее реализую
Код:
public class ProductManager
    {
        //Ссылка на главный менеджер
        private GeneralManager _generalManager;

        //Список товаров
        private List<Product> _ProductList;

        //Конструктор
        protected ProductManager()
        {
            _ProductList = new List<Product>();
            _generalManager = GeneralManager.GetUniqGeneralManager();
        }
       
        //Формируем одиночку
        private sealed class CreateProductManager
        {
            private static readonly ProductManager _uniqProductManager = new ProductManager();
            public static ProductManager GetManager
            {
                get
                {
                    return _uniqProductManager;
                }
            }

        //Инстанцируем или получаем одиночку
        public static ProductManager GetUniqProductManager()
        {
            return CreateProductManager.GetManager;
        }
.................................
//Метод получения ID последнего элемента
        public Int32 GetMaximumID()
        {
            if (_ProductList.Count() == 0)
                return -1;
            else
                return _ProductList.Last().ID;

        }
.................................
}
        }


И по такому же принципу главный менеджер
Код:
public class GeneralManager
{
       private ProductManager _prodManager;

        //Закрытый конструктор
        private GeneralManager()
        {
            _prodManager = ProductManager.GetUniqProductManager();
        }

        //Формируем одиночку
        private sealed class CreateGeneralManager
        {
            private static readonly GeneralManager _uniqGeneralManager = new GeneralManager();
            public static GeneralManager GetManager
            {
                get
                {
                    return _uniqGeneralManager;
                }
            }
        }

        //Инстанцируем или получаем одиночку
        public static GeneralManager GetUniqGeneralManager()
        {
            return CreateGeneralManager.GetManager;
        }
..........................
}

Все это дело при закрытии программы я начинаю сериализовать
 
Код:
FileStream exitFile = new FileStream("supermarket.bin", FileMode.Create);
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(exitFile, _mainGenManager);
            exitFile.Close();

Вот на этом месте пока все проходит нормально. Самое интересное начинается когда просиходит десириализация
Код:
//Ссылка на главный менеджер
        private GeneralManager _mainGenManager;
       
FileStream entry = new FileStream("supermarket.bin", FileMode.OpenOrCreate);
            if (entry.Length != 0)
            {
                BinaryFormatter bf = new BinaryFormatter();
                _mainGenManager = (GeneralManager)bf.Deserialize(entry);
            }
            else
                _mainGenManager = GeneralManager.GetUniqGeneralManager();
                 
            entry.Close();

            ProductManager test= ProductManager.GetUniqProductManager();

Тут думаю все понятно: сначала открываем файловый поток, потом указываем что десиарилизация будет из бинарного файла и собственно под конец загоняем всю структуру из файла в _mainGenManager; На этом месте нормальный ход вещей заканчивается...

Следом за отработавшей десиарилизацией я создаю ссылку на класс менеджера товаров и получаю его через спец.метод, возвращающий одиночку. После того как метод ProductManager.GetUniqProductManager()(кстати метод статичный) отработает у меня в указателе test оказывается не то что по идеи должно было десиализоваться, а пустой список(класс менеджера товаров инкапсулирует List<T>, которой хранит экземпляры класса товаров), хотя если обращаться к полю типа ProductManaget класса GeneralManager, то там как раз то, что нужно мне. И отсюда начинают вытекать все мои проблемы...

Собственно как это устранить?

Свой код пишу в Visual Studio 2008. Архив с проектом прикладываю к теме. Проект максимально утрировал, чтобы не загружать Вас.
Вот код [ATTACH]5151[/ATTACH]
5
22 мая 2011 года
hardcase
4.5K / / 09.08.2005
Вам не нужен синглтон. Откажитесь от идеи его использования в описанном сценарии.
Впрочим, если хотите его сериализовывать, то тыц.
8.9K
23 мая 2011 года
Apach47
130 / / 14.06.2010
Цитата: hardcase
Вам не нужен синглтон. Откажитесь от идеи его использования в описанном сценарии.


Реализация синглтона обязательна в рамках моего курсача, т.ч. придется искать пути. Спасибо за линк, буду разбираться

8.9K
23 мая 2011 года
Apach47
130 / / 14.06.2010
Нашел в сети такую реализацию синглетона
 
Код:
public enum Singleton7 {
    INSTANCE;

    private Singleton7() {
    }
 
    public static Singleton7 getInstance(){
        return INSTANCE;
    }
}

только что т я никак не могу нагуглить что такое INSTANCE
Линк на источник вот http://javatalks.ru/sutra58624.php#58624
5
23 мая 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Apach47
Нашел в сети такую реализацию синглетона
 
Код:
public enum Singleton7 {
    INSTANCE;

    private Singleton7() {
    }
 
    public static Singleton7 getInstance(){
        return INSTANCE;
    }
}

только что т я никак не могу нагуглить что такое INSTANCE
Линк на источник вот http://javatalks.ru/sutra58624.php#58624


INSTANCE это единственное поле в перечислении Singleton7 - так иногда реализуют синглтоны в Java.

5
23 мая 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Apach47
Реализация синглтона обязательна в рамках моего курсача, т.ч. придется искать пути. Спасибо за линк, буду разбираться


Т.е. использование нехорошего подхода к программированию ради использования нехорошего подхода? Если уж использовать синглтон, то какой смысл его сериализовывать? - сериализуйте данные, доступ к которым он предоставляет. Но, по сути такая схема будет мало чем отличаться от глобальных переменных (статических полей). Корректную реализацию синглтона нужно делать через DI контейнеры.

8.9K
23 мая 2011 года
Apach47
130 / / 14.06.2010
Цитата: hardcase
Т.е. использование нехорошего подхода к программированию ради использования нехорошего подхода?


Уважаемый hardcase, а чем собственно по Вашему мнению плох синглетон?

Цитата: hardcase
Если уж использовать синглтон, то какой смысл его сериализовывать? - сериализуйте данные, доступ к которым он предоставляет. Но, по сути такая схема будет мало чем отличаться от глобальных переменных (статических полей).


В курсаче предполагается примерно следующая структура
[ATTACH=CONFIG]5153[/ATTACH]
Представь какой бардак может наступить, если будет два менеджера, которые управляют двумя разными списками одной сущности, причем учти что через них еще идет согласование при добавлении и редактировании.
Наш препод нам так объясняла надобность синглетона, хотя оговаривалась, что в реальной жизни он редко где используется.

Цитата: hardcase
Корректную реализацию синглтона нужно делать через DI контейнеры.


Никогда раньше с этим не сталкивался)) Что кроме msdn можете рекомендовать почитать?

5
23 мая 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Apach47
Уважаемый hardcase, а чем собственно по Вашему мнению плох синглетон?

Тем, что синглтон на статических свойствах - это натуральная глобальная переменная, которой вы существенно ограничиваете гибкость вашей архитектуры.


Цитата: Apach47
Представь какой бардак может наступить, если будет два менеджера, которые управляют двумя разными списками одной сущности, причем учти что через них еще идет согласование при добавлении и редактировании.

Не вижу проблемы. По сути тебе статическая переменная требуется для инъекции зависимости Product-а от менеджера. С таким же успехом сущность-владельца можно передать, например, через конструктор сущности.

Цитата: Apach47
Наш препод нам так объясняла надобность синглетона, хотя оговаривалась, что в реальной жизни он редко где используется.

Отлично, а что если требуется работать с двумя такими файлами (например сравнить их содержимое)?

Цитата: Apach47
Никогда раньше с этим не сталкивался)) Что кроме msdn можете рекомендовать почитать?

DI на педивикии.

8.9K
26 мая 2011 года
Apach47
130 / / 14.06.2010
hardcase, да DI действительно более гибкая штука, но к критериях курсовой четко сказано что нужно реализовать Синглетон. Вот до чего я дошел с Вашей помощью
Код:
[Serializable]
    internal sealed class GeneralManagerSerializationHelper : IObjectReference
    {    // This object has no fields (although it could).

        // GetRealObject is called after this object is deserialized.
        public Object GetRealObject(StreamingContext context)
        {
            // When deserialiing this object, return a reference to
            // the Singleton object instead.
            return GeneralManager.GetUniqGeneralManager();
            //метод вызывается дважды
        }
    }

    [Serializable]
    public sealed class GeneralManager : ISerializable
    {
        public String _test;
        public Int32 _ttss;
        private static readonly GeneralManager _uniqGeneralManager = new GeneralManager();

        //Закрытый конструктор
        private GeneralManager()
        {
            /*_prodManager = ProductManager.GetUniqProductManager();
            _advManager = AdvertManager.GetUniqAdvertManager();
            _respManager = ResponsibleManager.GetUniqResponsibleManager();*/
            _test = "NEW";
            _ttss = 1111111;
        }

        //Инстанцируем или получаем одиночку
        public static GeneralManager GetUniqGeneralManager()
        {
            return _uniqGeneralManager;
        }

        //Перегрузка функции из интерфейса
        [SecurityPermissionAttribute(SecurityAction.LinkDemand,
            Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            // Instead of serializing this object,
            // serialize a SingletonSerializationHelp instead.
            info.SetType(typeof(GeneralManagerSerializationHelper));
            // No other values need to be added.
        }
    }


с помощью двух пабликовский полей я проверяю восстановилось ли состояние класса. В них перед сериализацией я закладываю
 
Код:
FileStream exitFile = new FileStream("supermarket.dat", FileMode.Create);
            BinaryFormatter bf = new BinaryFormatter();
            _mainGenManager._test = "TTT";
            _mainGenManager._ttss = 77777;
            bf.Serialize(exitFile, _mainGenManager);
            exitFile.Close();

Во время десиарилизации
 
Код:
BinaryFormatter bf = new BinaryFormatter();
                _mainGenManager = (GeneralManager)bf.Deserialize(entry);

я вижу что в полях _test и _ttss находятся значения, которые были им присвоены в конструкторе класса GeneralManager, а не при сериализации.

Единственное что я добился - метод GetUniqGeneralManager() стал отдавать ссылку на этот самый единственный экземпляр, а не создавать отдельную структуру.

Что у меня не так подскажите пожалуйста.

Проект для VS 2008 прилагаю [ATTACH]5161[/ATTACH]
5
26 мая 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Apach47
hardcase, да DI действительно более гибкая штука, но к критериях курсовой четко сказано что нужно реализовать Синглетон. Вот до чего я дошел с Вашей помощью....


Зачем все это? Почему бы не сделать совсем просто:

Код:
using System.IO;
using System.Runtime.CompilerServices;

public sealed class MySingleton
{
    private MySingleton()
    {
    }

    private static MySingleton _instance = null;
   
    public static MySingleton Instance
    {
        get
        {
            if(null == _instance)
                lock(typeof(MySingleton))
                {
                    if(null == _instance)
                        _instance = new MySingleton();
                }
            return _instance;
        }
    }

    [MethodImpl(MethodImplOptions.Synchronized)] // аналог lock(this){/* тело метода */}
    public void LoadState(Stream source)
    {
        // восстанавливаем содержимое из потока source
    }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public void SaveState(Stream target)
    {
        // сохраняем содержимое в поток target
    }
}
8.9K
26 мая 2011 года
Apach47
130 / / 14.06.2010
Под потоком Вы понимайте файловый поток в который или из которого записываются/читаются данные при сериализации/десиарилизации?
5
26 мая 2011 года
hardcase
4.5K / / 09.08.2005
Потоком может быть что угодно - память, файл, или вообще тело HTTP запроса/ответа.
8.9K
26 мая 2011 года
Apach47
130 / / 14.06.2010
А методы LoadState и SaveState предполагаются в классе синглетона или вне его?
Если вне, то это самый первый мой вариант синглетона с которого я начал. В нем создается более одной его копии... Вот поэтому я и начал лазеть по сети и пробовать разный изврат, который пока результата не принес
5
26 мая 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Apach47
А методы LoadState и SaveState предполагаются в классе синглетона или вне его?


Внутри. Сериализовывать сам синглтон очень неудобно (ты уже успел убедиться). Гораздо легче сериализовывать его внутреннее состояние (например некоторый внутренний DTO класс).

8.9K
29 мая 2011 года
Apach47
130 / / 14.06.2010
А можете объяснить/дать ссылку почему именно синглетон очень затруднительно сериализовывать?
5
29 мая 2011 года
hardcase
4.5K / / 09.08.2005
Цитата: Apach47
А можете объяснить/дать ссылку почему именно синглетон очень затруднительно сериализовывать?


Потому что если при десериализации окажется, что экземпляр синглтона уже существует, то нужно либо заменять существующий объект, либо менять состояние этого объекта. Неоднозначность этой операции можно разрешить способом который я предложил: сделать у класса синглтона методы LoadState и SaveState.

8.9K
30 мая 2011 года
Apach47
130 / / 14.06.2010
Цитата: hardcase
Потому что если при десериализации окажется, что экземпляр синглтона уже существует, то нужно либо заменять существующий объект, либо менять состояние этого объекта. Неоднозначность этой операции можно разрешить способом который я предложил: сделать у класса синглтона методы LoadState и SaveState.



Я понимаю что создается два экземпляра, а почему так? Почему два, а не три, четыре, десять?

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог