【问题标题】:protobuf-net inheritance shows base class in .proto fileprotobuf-net 继承在 .proto 文件中显示基类
【发布时间】:2017-07-28 08:23:25
【问题描述】:

我正在使用反射在运行时为 protobuf-net 构建运行时模型,而无需注释我需要序列化的类。

我需要序列化的一些类使用继承,当然我想要基类的所有属性。

protobuf-net 默认不抓取继承树,所以你需要告诉它基类。所以我写了一小段代码来做到这一点:

public class InheritanceTest
{
    public static string CreateProto()
    {
        var model = ProtoBuf.Meta.RuntimeTypeModel.Default;

        var type = typeof(SubClass);

        if (null != type.BaseType && type.BaseType != typeof(Object))
        {
            var hierarchy = new List<Type> { type };
            var baseType = type.BaseType;
            while (null != baseType)
            {
                if (baseType != typeof(Object))
                {
                    hierarchy.Add(baseType);
                }
                baseType = baseType.BaseType;
            }

            hierarchy.Reverse();
            var metaType = model.Add(hierarchy.First(), true);
            for (int i = 1; i < hierarchy.Count; i++)
            {
                model.Add(hierarchy[i], true);
                metaType = metaType.AddSubType(i, hierarchy[i]);
            }
        }
        else
        {
            model.Add(type, true);
        }

        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).OrderBy(p => p.Name);

        var tagNumber = 1;
        foreach (var propertyInfo in properties)
        {
            model[type].Add(tagNumber, propertyInfo.Name);
            tagNumber++;
        }

        var schema = model.GetSchema(type, ProtoSyntax.Proto3);
        return schema;
    }
}

public class BaseClass
{
    public string StringPropOnBaseClass { get; set; }
}

public class SubClass : BaseClass
{
    public string StringPropOnSubClass { get; set; }
}

这会产生一个像这样的 .proto 文件:

syntax = "proto3";
package ProtoBufferSerializerTest;

message BaseClass {
   // the following represent sub-types; at most 1 should have a value
   optional SubClass SubClass = 1;
}
message SubClass {
   string StringPropOnBaseClass = 1;
   string StringPropOnSubClass = 2;
}

为什么 BaseClass 包含在 .proto 文件中?没有理由需要将其引入公共电汇格式。

有没有办法告诉运行时模型不要将其包含在 .proto 文件中?

BR

【问题讨论】:

    标签: c# serialization protocol-buffers protobuf-net


    【解决方案1】:

    因为你告诉过它?

    如果我们将该代码更改为:

    Console.WriteLine("Adding: " + hierarchy.First().Name);
    var metaType = model.Add(hierarchy.First(), true);
    for (int i = 1; i < hierarchy.Count; i++)
    {
        Console.WriteLine("Adding: " + hierarchy[i].Name);
        model.Add(hierarchy[i], true);
        Console.WriteLine("Adding as sub type " + i + " to " + metaType.Type.Name);
        metaType = metaType.AddSubType(i, hierarchy[i]);
    }
    

    我们得到:

    Adding: BaseClass
    Adding: SubClass
    Adding as sub type 1 to BaseClass
    

    因此,您明确地将BaseClass 添加为合同类型,并且通过使用AddSubType,您可以明确地将它们连接到模型中。 protobuf 格式本身(Google 规范)不处理继承,因此任何 protobuf-net 都需要在其中工作,因此它通过可选的子对象模拟继承,从根开始 - 因为这是让您可靠地从 BaseClass 反序列化并使其有意义的唯一方法。有关它的作用的完整描述see this answer

    所以:如果您实际上打算在序列化中支持继承,那么在 .proto 中获得两种类型是正常的。如果您打算支持继承:*不要使用AddSubType。您可以将您需要的成员从基本类型*直接添加到SubClass

    public class InheritanceTest
    {
        static void Main()
        {
            Console.WriteLine(CreateProto());
    
            var obj = new SubClass {
                StringPropOnBaseClass = "abc",
                StringPropOnSubClass = "def" };
            var clone = Serializer.DeepClone(obj);
            Console.WriteLine(clone.StringPropOnBaseClass);
            Console.WriteLine(clone.StringPropOnSubClass);
        }
        public static string CreateProto()
        {
            var model = ProtoBuf.Meta.RuntimeTypeModel.Default;
    
            var metaType = model.Add(typeof(SubClass), false);
            metaType.AddField(1, "StringPropOnSubClass");
            metaType.AddField(2, "StringPropOnBaseClass");
    
            var schema =model.GetSchema(typeof(SubClass), ProtoSyntax.Proto3);
            return schema;
        }
    }
    

    哪个输出:

    syntax = "proto3";
    
    message SubClass {
       string StringPropOnSubClass = 1;
       string StringPropOnBaseClass = 2;
    }
    

    abc
    def
    

    顺便说一句,您在原始代码中将数字分配给子类型的方法显示了对哪些数字有效或可取的一些误解。 层次结构中的不同级别之间的字段编号不需要不同 - 因此在 5 级继承树中,如果他们愿意,他们都可以使用 1 作为子类型编号。但是每个子类型编号确实需要不与同一类型上的字段编号冲突。同样,链接的帖子对此进行了更详细的介绍。

    【讨论】:

    • 感谢您的回答。我最初要求运行时模型在添加类型时应用默认行为,但这意味着我没有在我的 .proto 文件中获得任何基类属性。答案是告诉它不要应用默认行为。然后我根本不用担心基类。非常感谢!
    • @JayPete 如果您的类型没有用属性(protocontract、datacontract 等)修饰,那么默认行为不存在无论如何
    • 很高兴知道!谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    • 2012-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多