C# Serialize inherited class with proto-buf 不使用 ProtoInclude

C# Serialize inherited class with proto-buf without use ProtoInclude

我有一个项目,用户可以为它开发插件。在他们的插件中,他们必须扩展一个抽象基础 class。在我项目的核心中,动态加载插件 (dll) 并新建这些 classes.

base class 代码核心:

[ProtoContract]
public abstract class BaseOutput
{
    public string OutputName {get;}
    [ProtoMember(1)]
    public int X { get; set; }
}

插件示例(在其他项目中):

[ProtoContract]
public class MyOutput : BaseOutput
{
    public override OutputName { get { return "MyOutput";} }
    [ProtoMember(11)]
    public double A { get; set; }
    [ProtoMember(12)]
    public double B { get; set; }
    [ProtoMember(13)]
    public double C { get; set; }
}

我知道我必须在 BaseOutput 上添加 [ProtoInclude(10, typeof(MyOutput))] 但是当我开发我的应用程序的核心时不知道用户将添加到程序中的插件。但我希望可以序列化所有 classes 使用 protobuf 扩展 BaseOutput。

解决方法是什么?

可以通过 RuntimeTypeModel API 在运行时使用 protobuf-net 配置这种类型的关系,如下例所示。但是:至关重要 重要的是所使用的密钥(示例中的42)是可靠且确定的每次您的应用程序运行。意思是:你需要某种方式可靠地为你的特定插件类型每次获取42,无论是否有人adds/removes 其他插件(这意味着:仅按字母顺序排列可能还不够)。另外:42 在加载的不同插件中必须是唯一的。


using ProtoBuf;
using ProtoBuf.Meta;

[ProtoContract]
public abstract class BaseOutput
{
    public abstract string OutputName { get; }
    [ProtoMember(1)]
    public int X { get; set; }
}

[ProtoContract]
public class MyOutput : BaseOutput
{
    public override string OutputName { get { return "MyOutput"; } }
    [ProtoMember(11)]
    public double A { get; set; }
    [ProtoMember(12)]
    public double B { get; set; }
    [ProtoMember(13)]
    public double C { get; set; }

    public override string ToString()
        => $"A={A}, B={B}, C={C}"; // to show working
}

class Program
{
    static void Main()
    {
        var pluginType = typeof(MyOutput); // after loading the plugin etc
        var baseType = RuntimeTypeModel.Default[typeof(BaseOutput)];
        baseType.AddSubType(42, pluginType);

        BaseOutput obj = new MyOutput { A = 1, B = 2, C = 3 };
        var clone = Serializer.DeepClone(obj);

        // outputs: A=1, B=2, C=3 - so: working (yay!)
        System.Console.WriteLine(clone.ToString());
    }
}