Странности при десериализации
Прошу помочь мне разобраться со следующей проблемой, которая проявляется у меня при десериализации.
Начну с писания задачи.
Имею три класса - товар, менеджер товаров и главным менеджер, а так же пару форм. В товаре описано поле типа менеджера товаров, через которое происходит обращение к методу этого класса для назначения id товару.
public Product(String name... **Констуктор с десятком параметров**)
{
//Сначала инициализируем менеджер
_myProductManager = ProductManager.GetUniqProductManager();
//Потом получаем последний элемент из списка товров, получаем его ID и прибавляем единицу
_id = _myProductManager.GetMaximumID() + 1;
........................................
}
.............................................................
Класс 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;
}
.................................
}
}
И по такому же принципу главный менеджер
{
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;
}
..........................
}
Все это дело при закрытии программы я начинаю сериализовать
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]
Впрочим, если хотите его сериализовывать, то тыц.
Реализация синглтона обязательна в рамках моего курсача, т.ч. придется искать пути. Спасибо за линк, буду разбираться
INSTANCE;
private Singleton7() {
}
public static Singleton7 getInstance(){
return INSTANCE;
}
}
только что т я никак не могу нагуглить что такое INSTANCE
Линк на источник вот http://javatalks.ru/sutra58624.php#58624
INSTANCE;
private Singleton7() {
}
public static Singleton7 getInstance(){
return INSTANCE;
}
}
только что т я никак не могу нагуглить что такое INSTANCE
Линк на источник вот http://javatalks.ru/sutra58624.php#58624
INSTANCE это единственное поле в перечислении Singleton7 - так иногда реализуют синглтоны в Java.
Т.е. использование нехорошего подхода к программированию ради использования нехорошего подхода? Если уж использовать синглтон, то какой смысл его сериализовывать? - сериализуйте данные, доступ к которым он предоставляет. Но, по сути такая схема будет мало чем отличаться от глобальных переменных (статических полей). Корректную реализацию синглтона нужно делать через DI контейнеры.
Уважаемый hardcase, а чем собственно по Вашему мнению плох синглетон?
В курсаче предполагается примерно следующая структура
[ATTACH=CONFIG]5153[/ATTACH]
Представь какой бардак может наступить, если будет два менеджера, которые управляют двумя разными списками одной сущности, причем учти что через них еще идет согласование при добавлении и редактировании.
Наш препод нам так объясняла надобность синглетона, хотя оговаривалась, что в реальной жизни он редко где используется.
Никогда раньше с этим не сталкивался)) Что кроме msdn можете рекомендовать почитать?
Тем, что синглтон на статических свойствах - это натуральная глобальная переменная, которой вы существенно ограничиваете гибкость вашей архитектуры.
Не вижу проблемы. По сути тебе статическая переменная требуется для инъекции зависимости Product-а от менеджера. С таким же успехом сущность-владельца можно передать, например, через конструктор сущности.
Отлично, а что если требуется работать с двумя такими файлами (например сравнить их содержимое)?
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.
}
}
с помощью двух пабликовский полей я проверяю восстановилось ли состояние класса. В них перед сериализацией я закладываю
BinaryFormatter bf = new BinaryFormatter();
_mainGenManager._test = "TTT";
_mainGenManager._ttss = 77777;
bf.Serialize(exitFile, _mainGenManager);
exitFile.Close();
Во время десиарилизации
_mainGenManager = (GeneralManager)bf.Deserialize(entry);
я вижу что в полях _test и _ttss находятся значения, которые были им присвоены в конструкторе класса GeneralManager, а не при сериализации.
Единственное что я добился - метод GetUniqGeneralManager() стал отдавать ссылку на этот самый единственный экземпляр, а не создавать отдельную структуру.
Что у меня не так подскажите пожалуйста.
Проект для VS 2008 прилагаю [ATTACH]5161[/ATTACH]
Зачем все это? Почему бы не сделать совсем просто:
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
}
}
Если вне, то это самый первый мой вариант синглетона с которого я начал. В нем создается более одной его копии... Вот поэтому я и начал лазеть по сети и пробовать разный изврат, который пока результата не принес
Внутри. Сериализовывать сам синглтон очень неудобно (ты уже успел убедиться). Гораздо легче сериализовывать его внутреннее состояние (например некоторый внутренний DTO класс).
Потому что если при десериализации окажется, что экземпляр синглтона уже существует, то нужно либо заменять существующий объект, либо менять состояние этого объекта. Неоднозначность этой операции можно разрешить способом который я предложил: сделать у класса синглтона методы LoadState и SaveState.
Я понимаю что создается два экземпляра, а почему так? Почему два, а не три, четыре, десять?