protobuf-net并使用接口序列化链表 [英] protobuf-net and serializing a linked list using interfaces
问题描述
我遇到了protobuf-net的问题,并将其范围缩小到了最简单的情况. 我想要一个链表类型结构,其中一个类具有相同类型的属性.当我序列化它时,它的效果很好.但是,如果类型是接口而不是类,则会出现以下错误: 为ConsoleApplication1.foo(ConsoleApplication1.ifoo)生成序列化程序后,无法更改类型.
I have come across a problem with protobuf-net and have narrowed it down to this simplest case. I want a linked list type structure, where a class has a property of the same type. When I serialize this it works great. However if the type is an interface instead of a class I get the following error: The type cannot be changed once a serializer has been generated for ConsoleApplication1.foo (ConsoleApplication1.ifoo)
这是我必须生成此错误的代码:
This is the code I have to generate this error:
class Program
{
static void Main(string[] args)
{
var toSerialize = new foo();
toSerialize.data = "foo1";
var subf = new foo();
subf.data = "foo2";
toSerialize.subfoo = subf;
using (var pbfsc = new FileStream("testfile.proto", FileMode.Create))
{
using (var cs = new GZipStream(pbfsc, CompressionMode.Compress))
{
ProtoBuf.Serializer.Serialize(cs, toSerialize);
}
pbfsc.Close();
}
}
}
[ProtoContract, ProtoInclude(2000, typeof(foo))]
public interface ifoo
{
[ProtoMember(1)]
string data { get; set; }
[ProtoMember(2)]
ifoo subfoo { get; set; }
}
[ProtoContract]
public class foo : ifoo
{
[ProtoMember(1)]
public string data { get; set; }
[ProtoMember(2)]
public ifoo subfoo { get; set; }
}
我已经尽我所能阅读了有关该主题的所有文章,但看不到我做错了什么.我尝试将我的对象放在包装器类中,因为它看起来与接口列表的问题类似(未显示代码),但这仍然无济于事.
I have done all the reading on the subject I can and I can't see what I have done wrong. I tried putting my object in a wrapper class as it looks similiar to a problem with lists of interfaces (code not shown) and this still didn't help.
如果我将subfoo更改为foo,那么它可以正常工作,但是在我更复杂的现实世界中,我宁愿坚持使用接口.我在这里做错了什么吗?还是protobuf-net有问题?
If I change subfoo to type foo then it works fine, but in my more complex real world problem I would rather stick to using interface. Have I done something wrong here or is this a problem with protobuf-net?
任何帮助都将不胜感激.
Any help greatly appreciated.
欢呼
亚历克斯
推荐答案
在实现作为合同接口的 top 对象时,需要完成一些出色的工作来改善对它们的处理. .基本上,目前,它仅处理根对象的非接口部分,但处理属性/子对象等的接口.
There is some outstanding work that needs completing to improve the treatment of the top level object when it implements an interface that is a contract. Basically, at the moment it only processes the non-interface parts of the root object, but handles interfaces for properties / sub-objects etc.
您看到的异常是轻微奇数-由于subfoo
属性,我希望它能及时了解ifoo
,但是可以通过以下方式避免出现基本问题:添加:
The exception you are seeing is slightly odd - I would expect it to have known about ifoo
in time, due to the subfoo
property, but the fundamental problem here can be avoided by adding:
Serializer.PrepareSerializer<ifoo>();
在序列化代码之前(理想情况是相当早;您只需要调用一次).
before the serialization code (ideally somewhere pretty early; you only need to call it once).
但是,我还应该注意,您实际上是在这里进行双序列化:序列化ifoo subfoo
属性时,它将序列化来自两个接口(ifoo
)的修饰成员和具体类型(foo
).这些值实际上是相同的,因此这里有一些冗余.
However, I should also note that you're actually double-serializing here: when serializing the ifoo subfoo
property, it will serialize the decorated members from both the interface (ifoo
) and the concrete type (foo
). These are actually the same values, so there is some redundancy here.
我会说:将成员级别的属性从具体类型中删除,但是根对象的故障会使此问题变得有些麻烦.解决两者问题(不再需要PrepareSerializer
)的另一种解决方法是:
I would say: take the member-level attributes off the concrete type, but the root-object glitch makes this slightly problematic. Another fix, that solves both issues (no longer requiring the PrepareSerializer
) is:
using ProtoBuf;
using System;
class Program
{
static void Main(string[] args)
{
var root = new foo();
root.data = "foo1";
var subf = new foo();
subf.data = "foo2";
root.subfoo = subf;
var toSerialize = new FooRoot { root = root };
// this does the same as your file-code, but runs
// both serialize and deserialize - basicaly, it is
// a lazy way of checking it end-to-end
var clone = Serializer.DeepClone(toSerialize).root;
Console.WriteLine(clone.data); // "foo1"
Console.WriteLine(clone.subfoo.data); // "foo2"
}
}
[ProtoContract]
public class FooRoot
{
[ProtoMember(1)]
public ifoo root { get; set; }
}
[ProtoContract, ProtoInclude(2000, typeof(foo))]
public interface ifoo
{
[ProtoMember(1)]
string data { get; set; }
[ProtoMember(2)]
ifoo subfoo { get; set; }
}
[ProtoContract]
public class foo : ifoo
{
public string data { get; set; }
public ifoo subfoo { get; set; }
}
当根对象/接口支持故障解决后,就不需要FooRoot
包装器了,但是我可能需要添加一些开关以启用/禁用此修复程序,以实现传统支持.
When the root-object/interface-support glitch is resolved, the FooRoot
wrapper would not be needed, but I'll probably need to add some switch to enable/disable the fix, for legacy support.
这篇关于protobuf-net并使用接口序列化链表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!