与 protobuf-net 和 C# 的接口

Posted

技术标签:

【中文标题】与 protobuf-net 和 C# 的接口【英文标题】:Interfaces with protobuf-net and C# 【发布时间】:2011-11-18 12:10:28 【问题描述】:

有谁知道为接口设置ProtoContract的正确方法是什么?

我得到以下异常“一旦生成序列化程序就无法更改类型”仅使用属性。

使用的代码:

    [ProtoContract]
    public class Lesson5TestClass2 : ILesson5TestInteface1
    
        [ProtoMember(1)]
        public string Name  get; set; 
        [ProtoMember(2)]
        public string Phone  get; set; 
    

    [ProtoContract]
    [ProtoInclude(1000, typeof(Lesson5TestClass2))]
    public interface ILesson5TestInteface1
    
        [ProtoMember(1)]
        string Name  get; set; 
        [ProtoMember(2)]
        string Phone  get; set; 
    

只有添加以下设置才能反序列化:

  RuntimeTypeModel.Default.Add(typeof (ILesson5TestInteface1), true)
      .AddSubType(50, typeof(Lesson5TestClass2));

我真的很想只使用属性来配置它。

我正在使用 NuGet 的 protobuf-net r470。

顺便说一句:这个例子来自一组“通过测试的教训”,展示了如何为我的同事使用 protobuf-net 进行序列化。

感谢阅读:)

【问题讨论】:

为什么需要 DTO 的接口? @jgauffin:我只是在编写一些代码来了解 protobuf-net 的功能。第 5 课是关于接口的。 :) 【参考方案1】:

有趣;是的,上面好像有什么东西。但是,它确实在作为成员公开时起作用,即

[ProtoContract]
class Wrapper

    [ProtoMember(1)]
    public ILesson5TestInteface1 Content  get; set; 

static class Program

    static void Main()
    
        Wrapper obj = new Wrapper
        
            Content = new Lesson5TestClass2()
        , clone;
        using(var ms = new MemoryStream())
        
            Serializer.Serialize(ms, obj);
            ms.Position = 0;
            clone = Serializer.Deserialize<Wrapper>(ms);
        
        // here clone.Content *is* a Lesson5TestClass2 instance
    

我将不得不看看作为 root 对象的接口支持是什么。

【讨论】:

是的,当您从未使用过反序列化程序时,似乎根对象存在问题。我已经编写了两个测试,一个使用包装器,一个没有包装器。如果带包装器的测试首先运行,则不带包装器的测试通过。但是如果没有包装器的测试首先运行,两个测试都会失败。 :? @graffic 最好的办法是记录一个错误,我会进行调查。很好奇。 好吧 :) 星期五晚上在这里,所以也许你必须等到星期一(无论如何我会尽力而为)。同时...我会接受您的正确回答以结束此问题。 马克,这个问题在 v2 中得到修复了吗?我正在使用 r480 并遇到同样的问题。 @Eric v1 从不支持接口。在 v2 中,它们仍然只作为 成员 工作。我必须解决这个问题...【参考方案2】:

对我有用的最简单的解决方案是在序列化实际实例之前让 Protobuf 了解接口。

var obj = new Lesson5TestClass2();
using var ms = new MemoryStream();

// vvv - Add this line
Serializer.PrepareSerializer<ILesson5TestInteface1>();

Serializer.Serialize(ms, obj);
ms.Position = 0;
var clone = Serializer.Deserialize<ILesson5TestInteface1>(ms);

我猜想这个调用会使 Protobuf 序列化 Lesson5TestClass2 作为 ILesson5TestInteface1 的实例,否则它不会这样做,因为它没有机会知道 ILesson5TestInteface1

如果您按照另一个答案的建议使用包装器class,这可能也会发生(然后 Protobuf 知道接口,因为它是在序列化之前检查的一种属性)。

我真的很想只使用属性来配置它。

这个解决方案几乎是你想要的,但仍然需要每个接口调用一次 PrepareSerializer(这比 RuntimeTypeModel.Default.Add 更好,因为它是每个实现一次)。但是,您可以轻松地创建一个帮助方法来每次调用 PrepareSerializer + Serialize 组合(缓存不会造成性能损失),它应该可以正常工作,无需额外配置。

【讨论】:

以上是关于与 protobuf-net 和 C# 的接口的主要内容,如果未能解决你的问题,请参考以下文章

协议缓冲区 c# (protobuf-net) Message::ByteSize

protobuf-net - 反引号、字典和 .proto 文件

通过 C# 和 Java 之间的 protobuf-net 进行序列化/解封

使用 protobuf-net 进行质量过滤

关于在 C# 中使用 Protobuf-Net

protobuf-net 不适合哪些场景?