3

我遇到了 protobuf-net 的问题,并将其缩小到这个最简单的情况。我想要一个链表类型结构,其中一个类具有相同类型的属性。当我序列化它时,它工作得很好。但是,如果类型是接口而不是类,我会收到以下错误:一旦为 ConsoleApplication1.foo (ConsoleApplication1.ifoo) 生成了序列化程序,就无法更改类型

这是我必须生成此错误的代码:

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; }
}

我已经完成了所有关于该主题的阅读,但我看不出我做错了什么。我尝试将我的对象放在一个包装类中,因为它看起来类似于接口列表的问题(代码未显示),但这仍然没有帮助。

如果我将 subfoo 更改为 foo 类型,那么它可以正常工作,但在我更复杂的现实世界问题中,我宁愿坚持使用界面。我在这里做错了什么还是protobuf-net有问题?

非常感谢任何帮助。

干杯

亚历克斯

4

1 回答 1

1

当顶层对象实现一个契约接口时,需要完成一些出色的工作来改进对顶层对象的处理。基本上,目前它只处理根对象的非接口部分,但处理属性/子对象等的接口。

您看到的异常有点奇怪ifoo-由于属性,我希望它及时知道subfoo,但是可以通过添加以下内容来避免这里的基本问题:

Serializer.PrepareSerializer<ifoo>();

在序列化代码之前(理想情况下很早;你只需要调用一次)。

但是,我还应该注意,您实际上在这里进行了双重序列化:当序列化ifoo subfoo属性时,它将从接口( ifoo)具体类型 ( foo) 序列化修饰的成员。这些实际上是相同的值,所以这里有一些冗余。

想说:从具体类型中去掉成员级别的属性,但是根对象故障使这有点问题。解决这两个问题(不再需要PrepareSerializer)的另一个修复是:

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将不需要包装器,但我可能需要添加一些开关来启用/禁用修复,以获得旧版支持。

于 2012-09-28T06:32:27.157 回答