了解 'serializing by reference' 的概念
Understanding the concept of 'serializing by reference'
我正在编写自己的针对游戏开发优化的二进制序列化程序。到目前为止,它功能齐全。它发出 IL 以生成预先给定一系列类型的 [反] 序列化方法。唯一缺少的功能是通过引用序列化事物,目前一切都按值序列化。
要实现,我得先了解一下。这就是我发现有点棘手的地方。让我告诉你我在这几个例子中的理解:
示例 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
的 id。
但是;看看第二个例子:
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
的 id,而不是按值序列化它。
运行 代码和查看输出似乎并非如此。在第二个输出中,我们看到序列化程序按值序列化 p2
,就好像它还没有遇到它一样......我没有得到。就像内部有一个 ID 计数器在 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
,任何支持按引用序列化的序列化程序都可以工作。
我曾尝试在 DataContractSerializer
上使用 ILSpy,但我很快就迷路了,什么也搞不清楚。
- 在
Example2
中,为什么序列化器没有存储一个id到
p1.Friend
序列化时 p2
? - 是 'serializing by reference'
仅适用于单个对象层次结构,或者它如何在
将军?
- 在我看来,按引用序列化会自动
处理循环引用 (A <-> B),对吗?或者我需要
做其他事情以确保我不会陷入无限循环?
- 我假设按引用序列化只有在应用时才有意义
关于引用类型而不是值类型,对吗?
我标记了 protobuf-net,因为它的相似之处在于它是一个二进制序列化程序并发出 IL。我很想听听那里是如何通过引用实现序列化的:p
- 每次调用 write-object 都是一个单独的序列化上下文;调用之间不保留引用跟踪
- 只要您正确识别以前看到的值,它就不会递归,但深度检查可以帮助避免问题
- 正确,尽管如果您愿意,您可以尝试识别语义相同的值类型(可能是结构相等接口)
其他想法:如果将其应用于字符串,您可能希望将特殊情况作为有效相等而不是引用相等 - 序列化两个不同的实例(引用)毫无意义相同的字符串
我正在编写自己的针对游戏开发优化的二进制序列化程序。到目前为止,它功能齐全。它发出 IL 以生成预先给定一系列类型的 [反] 序列化方法。唯一缺少的功能是通过引用序列化事物,目前一切都按值序列化。
要实现,我得先了解一下。这就是我发现有点棘手的地方。让我告诉你我在这几个例子中的理解:
示例 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
的 id。
但是;看看第二个例子:
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
的 id,而不是按值序列化它。
运行 代码和查看输出似乎并非如此。在第二个输出中,我们看到序列化程序按值序列化 p2
,就好像它还没有遇到它一样......我没有得到。就像内部有一个 ID 计数器在 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
,任何支持按引用序列化的序列化程序都可以工作。
我曾尝试在 DataContractSerializer
上使用 ILSpy,但我很快就迷路了,什么也搞不清楚。
- 在
Example2
中,为什么序列化器没有存储一个id到p1.Friend
序列化时p2
? - 是 'serializing by reference' 仅适用于单个对象层次结构,或者它如何在 将军? - 在我看来,按引用序列化会自动 处理循环引用 (A <-> B),对吗?或者我需要 做其他事情以确保我不会陷入无限循环?
- 我假设按引用序列化只有在应用时才有意义 关于引用类型而不是值类型,对吗?
我标记了 protobuf-net,因为它的相似之处在于它是一个二进制序列化程序并发出 IL。我很想听听那里是如何通过引用实现序列化的:p
- 每次调用 write-object 都是一个单独的序列化上下文;调用之间不保留引用跟踪
- 只要您正确识别以前看到的值,它就不会递归,但深度检查可以帮助避免问题
- 正确,尽管如果您愿意,您可以尝试识别语义相同的值类型(可能是结构相等接口)
其他想法:如果将其应用于字符串,您可能希望将特殊情况作为有效相等而不是引用相等 - 序列化两个不同的实例(引用)毫无意义相同的字符串