2015-05-01 2 views
1

Я пишу свой собственный бинарный сериализатор, оптимизированный для разработки игр. Пока это полностью функционально. Он испускает IL для генерации методов сериализации [de] с заданной последовательностью типов заранее. Единственная пропущенная функция - сериализация вещей по ссылке, все в настоящее время сериализуется по значению.Понимание концепции «сериализации по ссылке»

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

Пример 1 (как показано here):

public class Person 
{ 
    public string Name; 
    public Person Friend; 
} 

static void Main(string[] args) 
{ 
    Person p1 = new Person(); 
    p1.Name = "John"; 

    Person p2 = new Person(); 
    p2.Name = "Mike"; 

    p1.Friend = p2; 

    Person[] group = new Person[] { p1, p2 }; 

    var serializer = new DataContractSerializer(group.GetType(), null, 
     0x7FFF /*maxItemsInObjectGraph*/, 
     false /*ignoreExtensionDataObject*/, 
     true /*preserveObjectReferences : this is where the magic happens */, 
     null /*dataContractSurrogate*/); 

    serializer.WriteObject(Console.OpenStandardOutput(), group); 
} 

Теперь это полностью понят. У нас есть корневой объект, который является массивом, ссылаясь на двух уникальных лиц. p1.Friend - p2. Поэтому вместо сериализации p1.Friend по значению мы просто храним идентификатор, который указывает на p2, который мы уже сериализуем.

Однако; посмотрим на второй пример:

static void Example2() 
    { 
     var p1 = new Person() { Name = "Diablo" }; 
     var p2 = new Person() { Name = "Mephesto" }; 

     p1.Friend = p2; 

     var serializer = new DataContractSerializer(typeof(Person), null, 0x7FFF, false, true, null); 

     serializer.WriteObject(Console.OpenStandardOutput(), p1); 
     Console.WriteLine("\n"); 
     serializer.WriteObject(Console.OpenStandardOutput(), p2); 
    } 

Теперь, согласно моему пониманию: когда сериализации p1 сериализатор будет сериализовать p1.Name и p1.Friend. Во втором WriteObject сериализатор уже сериализовался p2 (который равен p1.Friend), поэтому он просто сериализует идентификатор, который указывает на p1.Friend вместо сериализации его по значению.

Запуск кода и просмотр выходных данных, похоже, не так. На втором выходе мы видим сериализатор сериализации p2 по значению, как будто он еще не наткнулся на него ... И этого я не получил. Это как есть счетчик идентификатор внутри, который получает сброс в конце WriteObject

enter image description here

Вот еще похожий пример:

static void Example3() 
    { 
     var p1 = new Person() { Name = "Diablo" }; 
     var p2 = p1; 

     var serializer = new DataContractSerializer(typeof(Person), null, 0x7FFF, false, true, null); 

     serializer.WriteObject(Console.OpenStandardOutput(), p1); 
     Console.WriteLine("\n"); 
     serializer.WriteObject(Console.OpenStandardOutput(), p2); 
    } 

Опять же, второй вывод показывает, что мы сериализации p2, как если бы мы пока не нашли для него определения.

Обратите внимание, что я не выбрал DataContractSerializer по любой конкретной причине, любой сериализатор, поддерживающий сериализацию по ссылке, работает.

Я попытался установить ILSpy на DataContractSerializer, но я быстро потерялся и не мог понять много.

  1. В Example2, почему не сериализатору хранить идентификатор p1.Friend при сериализации p2? - Является ли «сериализация по ссылке» применяется только к одной иерархии объектов или как она работает в вообще?
  2. Мне кажется, что сериализация по ссылке будет автоматически ручная циркулярная ссылка (A < -> B), это правильно? или мне нужно делать другие вещи, чтобы я не попал в бесконечный цикл?
  3. Я предполагаю, что сериализация по ссылке имеет смысл только при применении на ссылочных типах, а не на значениях, правильно?

Я пометил protobuf-net, потому что он похож на то, что он является двоичным сериализатором и испускает IL. Я хотел бы услышать, как там реализуется сериализация по ссылке: p

+0

Замечание: вам не нужно использовать ILSpy, вы можете посмотреть здесь код - http://referencesource.microsoft.com/#System.Runtime.Serialization/System/Runtime/Serialization/DataContractSerializer.cs –

ответ

2
  1. Каждый вызов объекта записи представляет собой отдельный контекст сериализации; ссылка отслеживание не сохраняется между вызовами
  2. Пока вы правильно определить ранее увиденные ценности, он не должен получить рекурсивным, но проверка глубины может помочь избежать проблем
  3. Правильно, хотя может попытки признать семантически идентичные типов значений, если вы хотите (возможно, структурный интерфейс равенства)

Дополнительной мысль: если применить это строку, вы можете специальный случай, как эффективного равенство, а не равенство ссылок - не точка serialising два разных экземпляра (ссылки) одной и той же строки

+0

Спасибо за ответ. On 1- Это стандарт во всех сериализаторах, которые реализуют сериализацию по ref? или только в DataContractSerializer? означает ли это, что в примерах 2 и 3 нет способа сериализовать второй объект по ссылке? или это не имеет смысла, и это часть моего недоразумения? – vexe

+1

@vexe это почти стандарт во всех сериализаторах; вы * можете * иметь возможность найти тот, у которого есть API для сохранения такого, но это ** не ** ожидаемый сценарий; он * ожидает, что каждый вызов 'Serialize' /' Write'/whatever не связан и не разделен. В частности, если вы вызываете 'Write (obj, target1)' и 'Write (obj, target2)', обычно ожидается, что 'target1' и' target2' будут иметь ** идентичное ** содержимое (или, по крайней мере: эквивалентное содержимое). Этого не может быть, если во второй раз он просто говорит: «Я видел эти объекты раньше: я просто сериализую идентификаторы» –

+0

Я думал об использовании protobuf-net и компилировал dll для типа с полями, отмеченными значком [ AsReference] (если я правильно помню), а затем просмотр сгенерированного кода. У него должна быть правильная логика «serialize by reference», испеченная в dll? поэтому я могу, может быть, получить общее представление о том, как это сделать – vexe

Смежные вопросы