Я пишу свой собственный бинарный сериализатор, оптимизированный для разработки игр. Пока это полностью функционально. Он испускает 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
Вот еще похожий пример:
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
, но я быстро потерялся и не мог понять много.
- В
Example2
, почему не сериализатору хранить идентификаторp1.Friend
при сериализацииp2
? - Является ли «сериализация по ссылке» применяется только к одной иерархии объектов или как она работает в вообще? - Мне кажется, что сериализация по ссылке будет автоматически ручная циркулярная ссылка (A < -> B), это правильно? или мне нужно делать другие вещи, чтобы я не попал в бесконечный цикл?
- Я предполагаю, что сериализация по ссылке имеет смысл только при применении на ссылочных типах, а не на значениях, правильно?
Я пометил protobuf-net, потому что он похож на то, что он является двоичным сериализатором и испускает IL. Я хотел бы услышать, как там реализуется сериализация по ссылке: p
Замечание: вам не нужно использовать ILSpy, вы можете посмотреть здесь код - http://referencesource.microsoft.com/#System.Runtime.Serialization/System/Runtime/Serialization/DataContractSerializer.cs –