Проблема с Object Serialization
От: Konstantin Orlov  
Дата: 12.02.02 12:27
Оценка:
Краткое описание проблемы:
Работаю с сохранением объектов в .Net (на C#).
Использую двоичное сохранение (BinaryFormatter).
Есть две переменных класса: foo1 и foo2, в котором есть поле типа "object" (путь m_ob).
В эти две переменные заношу "адрес" третьего класса fooTarget:
  foo1.m_ob = fooTarget
  foo2.m_ob = fooTarget

В итоге справедливо такое отношение:
  foo1.m_ob == foo2.m_ob          //!!!

Делаю сохраниние первых двух классов (seriazation) в двоичный файл. Примечание: все три класса помечены как сохраняемые (Seriazable). Т.е. вызываю примерно так:
   formatter.Serialize(stream, foo1);
   formatter.Serialize(stream, foo2);

Все сохраняется ОК.
Далее пытаюсь прочитать (Deserialize) эти два класса из этого же файла:
   foo1 = (CFoo)formatter.Deserialize(stream);
   foo2 = (CFoo)formatter.Deserialize(stream);

Оба они восстанавливаются без ошибок, Но! Происходит следующее:
foo1.m_ob != foo2.m_ob          //!!!

Т.е. третий класс, на который ссылаются обе переменных, создается два раза.

Теперь вопрос: как мне добиться того, чтобы сохранялась ссылочная целостность, т.е. после восстановления обе переменных ссылались на один и тот же класс.
Re: Проблема с Object Serialization
От: Lexey Россия  
Дата: 12.02.02 12:58
Оценка:
Здравствуйте Konstantin Orlov, Вы писали:

KO>Краткое описание проблемы:

KO>Работаю с сохранением объектов в .Net (на C#).
KO>Использую двоичное сохранение (BinaryFormatter).
KO>Есть две переменных класса: foo1 и foo2, в котором есть поле типа "object" (путь m_ob).
KO>В эти две переменные заношу "адрес" третьего класса fooTarget:
KO>
KO>  foo1.m_ob = fooTarget
KO>  foo2.m_ob = fooTarget
KO>

KO>В итоге справедливо такое отношение:
KO>
KO>  foo1.m_ob == foo2.m_ob          //!!!
KO>

KO>Делаю сохраниние первых двух классов (seriazation) в двоичный файл. Примечание: все три класса помечены как сохраняемые (Seriazable). Т.е. вызываю примерно так:
KO>
KO>   formatter.Serialize(stream, foo1);
KO>   formatter.Serialize(stream, foo2);
KO>

KO>Все сохраняется ОК.
KO>Далее пытаюсь прочитать (Deserialize) эти два класса из этого же файла:
KO>
KO>   foo1 = (CFoo)formatter.Deserialize(stream);
KO>   foo2 = (CFoo)formatter.Deserialize(stream);
KO>

KO>Оба они восстанавливаются без ошибок, Но! Происходит следующее:
KO>
KO>foo1.m_ob != foo2.m_ob          //!!!
KO>

KO>Т.е. третий класс, на который ссылаются обе переменных, создается два раза.

Логично, блин. Сериализация ведь идет по значению и десериализация тоже.

KO>Теперь вопрос: как мне добиться того, чтобы сохранялась ссылочная целостность, т.е. после восстановления обе переменных ссылались на один и тот же класс.


Сдается мне, что придется тебе написать что-то вроде моникеров. Т.е. держать некую коллекцию объектов, каждому из которых присвоен уникальный идентификатор (например, GUID). Все объекты m_ob хранить в этой коллекции, а в foo хранить объект-моникер, хранящий идентификатор. При сериализации foo сохранять только этот идентификатор, а коллекцию сериализовать отдельно.
Re[2]: Проблема с Object Serialization
От: Konstantin Orlov  
Дата: 12.02.02 13:53
Оценка:
Здравствуйте Lexey, Вы писали:

L>Логично, блин. Сериализация ведь идет по значению и десериализация тоже.


Но мне тут подсказывают, что в MFC сериализация была по ссылке! Так почему в .Net у них этого нет. Ведь это сделать было гораздо проще — у них есть коллекция объектов, они сами ответчают за расположение объектов в памяти и т.д.

L>Сдается мне, что придется тебе написать что-то вроде моникеров. Т.е. держать некую коллекцию объектов, каждому из которых присвоен уникальный идентификатор (например, GUID). Все объекты m_ob хранить в этой коллекции, а в foo хранить объект-моникер, хранящий идентификатор. При сериализации foo сохранять только этот идентификатор, а коллекцию сериализовать отдельно.


По причинами работы программы это пока не представляется возможным. Я сначала так и хотел сделать, но это резко усложняет написание приложений для программы.
Re[3]: Проблема с Object Serialization
От: Lexey Россия  
Дата: 12.02.02 14:56
Оценка:
Здравствуйте Konstantin Orlov, Вы писали:

KO>Здравствуйте Lexey, Вы писали:


L>>Логично, блин. Сериализация ведь идет по значению и десериализация тоже.


KO>Но мне тут подсказывают, что в MFC сериализация была по ссылке! Так почему в .Net у них этого нет. Ведь это сделать было гораздо проще — у них есть коллекция объектов, они сами ответчают за расположение объектов в памяти и т.д.


Тут основная проблема в том, что то, что ты хочешь практически невозможно реализовать так, чтобы оно работало во всех случаях жизни. Подумай сам, откуда сериализатор может знать, как ему сохранять объект — как ссылку или как значение. Допустим ты хочешь, чтобы первый раз сохранилось значение, а затем ссылки -ОК. Но тогда сериализатор должен четко себе представлять, какой раз является первым, а какие — нет. А такого представления у него нет и быть не может, т.к. ты можешь один и тот же объект сохранять в разные стримы. Если же пытаться для каждого объекта отслеживать его сохранение в конкретный стрим, то архитектура сериализации превратится в какого-то жуткого монстра.

Брр... в MFC сериализация всю жизнь была ручная, т.е. как напишешь, так и будет. Я вообще не представляю себе сериализацию (как persistence, а не как marshaling) по ссылке.

L>>Сдается мне, что придется тебе написать что-то вроде моникеров. Т.е. держать некую коллекцию объектов, каждому из которых присвоен уникальный идентификатор (например, GUID). Все объекты m_ob хранить в этой коллекции, а в foo хранить объект-моникер, хранящий идентификатор. При сериализации foo сохранять только этот идентификатор, а коллекцию сериализовать отдельно.


KO>По причинами работы программы это пока не представляется возможным. Я сначала так и хотел сделать, но это резко усложняет написание приложений для программы.
Re: Проблема с Object Serialization
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.02.02 19:21
Оценка:
Здравствуйте Konstantin Orlov, Вы писали:

KO>Краткое описание проблемы:

KO>Т.е. третий класс, на который ссылаются обе переменных, создается два раза.

KO>Теперь вопрос: как мне добиться того, чтобы сохранялась ссылочная целостность, т.е. после восстановления обе переменных ссылались на один и тот же класс.


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

[Serializable]
public class SerObj {
}

[Serializable]
public class SerObjHolder {
 public SerObj obj1;
 public SerObj obj2;
 public void WriteEquality() {
  Console.WriteLine("obj1 == obj2 {0}",(obj1 == obj2));
 }
}

public class test {
 static void Main() {
  SerObj so = new SerObj();
  SerObjHolder soh = new SerObjHolder();
  soh.obj1 = so;
  soh.obj2 = so;
  soh.WriteEquality();

  IFormatter formatter = new BinaryFormatter();
  Stream stream = new FileStream("ser.bin", FileMode.Create, FileAccess.Write, FileShare.None);
  formatter.Serialize(stream,soh);
  stream.Close();

  stream = new FileStream("ser.bin", FileMode.Open, FileAccess.Read, FileShare.Read);
  soh = (SerObjHolder) formatter.Deserialize(stream);
  stream.Close();
  soh.WriteEquality();
 }
}


Консоль:
obj1 == obj2 True
obj1 == obj2 True
AVK Blog
Re[2]: Проблема с Object Serialization
От: Konstantin Orlov  
Дата: 13.02.02 05:55
Оценка:
Здравствуйте AndrewVK, Вы писали:

AVK>Здравствуйте Konstantin Orlov, Вы писали:


AVK>[Serializable]
AVK>public class SerObjHolder {
AVK> public SerObj obj1;
AVK> public SerObj obj2;
AVK>}
...
AVK> SerObjHolder soh;
AVK> formatter.Serialize(stream,soh);
...


Вот именно в этом случае это работает как надо! Как я понял, Formatter написан так, чтобы сохранять ссылочную целостность при одном вызове метода Serialize. Т.е. при сериализации дерева объектов, корневой объект которого сериализуется, сохраняется ссылочная целостность. При следующем вызове метода Serialize сохраняется другое дерево объектов, независимо от того, были ли они сохранены ранее.
Таким образом, класс написан так, чтобы делать то, что я хочу (см. выше) при одноразовом вызове метода класса Formatter, но не сделано так, чтобы это поддерживалось для экземпляра класса Formatter. Хоть бы написали это жирными большими буквами.
Спасибо всем большое за помощь. Теперь я смогу решить эту проблему.

Но есть еще один вопрос. Я сохранял объект TreeNode (описывает один узел в контроле TreeView). Он описан как Seriazable, т.е. поддерживает сериализацию.
Когда этот объект не имеет дочерних объектов (поле Nodes с типом TreeNodeCollection), то он сохраняется и восстанавливается без ошибок, но если у него дочерние объекты, то происходит сбой при десериализации!
При этом вываливается исключение:
'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
Во вложенном exception находится следующее:
"Object reference not set to an instance of an object."

В чем я неправ?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.