Protobuf属性具有generics类的层次结构

我有一个类似于此的类层次结构。 这些类包含我已排除的许多其他细节。 这是一个简化,专注于这些类的序列化方面。

[ProtoInclude(1, typeof(Query))] [ProtoInclude(2, typeof(Query))] [ProtoInclude(3, typeof(Query))] [ProtoInclude(4, typeof(Query))] [ProtoInclude(5, typeof(Query))] abstract class Query { public string Result { get; set; } } [ProtoInclude(1, typeof(SpecialQuery)] abstract class Query : Query { public new T Result { get; set; } } abstract class SpecialQuery : Query { public new string Result { get; set; } } 

我还有大约150个自动生成的genericsQuery后代,有各种各样的generics类型。 例如:

 [ProtoContract] class W : Query { } [ProtoContract] class X : Query { } [ProtoContract] class Y : Query { } [ProtoContract] class Z : SpecialQuery { } 

我还为所有这些类型自动生成了[ProtoInclude]。 例如:

 [ProtoInclude(1, typeof(W)] [ProtoInclude(2, typeof(X)] [ProtoInclude(3, typeof(Y)] [ProtoInclude(4, typeof(Z)] 

问题是,如何部署这150个ProtoIncludes? 我尝试了各种似乎合乎逻辑的组合,但是我会得到各种例外,具体取决于哪些属性存在于哪里。 在上面的例子中实际需要序列化的类型是W,X,Y,Z,只有大约150个。

protobuf-net甚至可以处理这样的事情,还是应该尝试其他类型的序列化?

好; 随着更新的问题,我理解了一点。 我希望对象模型中间的generics确实会让生活变得“有趣”。 它不能“开箱即用”; 我看看是否有一些简单的调整我可以支持它,但它开始变得丑陋很快。 我希望如果可能的话,简单地在中间删除对generics的需求会更好 – 可能保留通用接口 (而不是generics类)。 这是一些有效的代码; 这如何映射到你的代码…我不能100%告诉你。 请注意,您不必使用TypeDescriptor (等) – 它似乎只是因为您使用的是代码,这可能会使某些事情变得更容易……

(我没有检查DataSet东西 – 只是类的东西)

 using System; using System.ComponentModel; using System.Data; using System.IO; using NUnit.Framework; using ProtoBuf; [TestFixture] public class ComplexGenericTest { [Test] public void TestX() { Query query = new X { Result = "abc" }; Assert.AreEqual(typeof(string), query.GetQueryType()); Query clone = Serializer.DeepClone(query); Assert.IsNotNull(clone); Assert.AreNotSame(clone, query); Assert.IsInstanceOfType(query.GetType(), clone); Assert.AreEqual(((X)query).Result, ((X)clone).Result); } [Test] public void TestY() { Query query = new Y { Result = 1234}; Assert.AreEqual(typeof(int), query.GetQueryType()); Query clone = Serializer.DeepClone(query); Assert.IsNotNull(clone); Assert.AreNotSame(clone, query); Assert.IsInstanceOfType(query.GetType(), clone); Assert.AreEqual(((Y)query).Result, ((Y)clone).Result); } } public static class QueryExt { public static Type GetQueryType(this IQuery query) { if (query == null) throw new ArgumentNullException("query"); foreach (Type type in query.GetType().GetInterfaces()) { if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IQuery<>)) { return type.GetGenericArguments()[0]; } } throw new ArgumentException("No typed query implemented", "query"); } } public interface IQuery { string Result { get; set; } } public interface IQuery : IQuery { new T Result { get; set; } } [ProtoInclude(21, typeof(W))] [ProtoInclude(22, typeof(X))] [ProtoInclude(23, typeof(Y))] [ProtoInclude(25, typeof(SpecialQuery))] [ProtoContract] abstract class Query : IQuery { public string Result { get { return ResultString; } set { ResultString = value; } } protected abstract string ResultString { get; set; } // these are to allow simple ResultString implementations // without the codegen having to worry about int.Parse etc protected static string FormatQueryString(T value) { return TypeDescriptor.GetConverter(typeof(T)) .ConvertToInvariantString(value); } protected static T ParseQueryString(string value) { return (T) TypeDescriptor.GetConverter(typeof(T)) .ConvertFromInvariantString(value); } } [ProtoContract] [ProtoInclude(21, typeof(Z))] abstract class SpecialQuery : Query, IQuery { public new DataSet Result { get; set; } [ProtoMember(1)] protected override string ResultString { get { if (Result == null) return null; using (StringWriter sw = new StringWriter()) { Result.WriteXml(sw, XmlWriteMode.WriteSchema); return sw.ToString(); } } set { if (value == null) { Result = null; return; } using (StringReader sr = new StringReader(value)) { DataSet ds = new DataSet(); ds.ReadXml(sr, XmlReadMode.ReadSchema); } } } } [ProtoContract] class W : Query, IQuery { [ProtoMember(1)] public new bool Result { get; set; } protected override string ResultString { get {return FormatQueryString(Result); } set { Result = ParseQueryString(value); } } } [ProtoContract] class X : Query, IQuery { [ProtoMember(1)] public new string Result { get; set; } protected override string ResultString { get { return Result ; } set { Result = value; } } } [ProtoContract] class Y : Query, IQuery { [ProtoMember(1)] public new int Result { get; set; } protected override string ResultString { get { return FormatQueryString(Result); } set { Result = ParseQueryString(value); } } } [ProtoContract] class Z : SpecialQuery { } 

我不是100%确定我理解你想要建模的场景; 但是, [ProtoInclude]只看到一级inheritance。

如果我理解正确,请尝试以下方法; 请注意,您需要在编译时知道潜在的generics类型:

 using System; using ProtoBuf; [ProtoContract] [ProtoInclude(2, typeof(Response))] [ProtoInclude(3, typeof(Query))] class Packet { [ProtoMember(1)] int ID; } [ProtoContract] [ProtoInclude(1, typeof(Response))] [ProtoInclude(2, typeof(Response))] [ProtoInclude(3, typeof(Response))] class Response : Packet { } [ProtoContract] class Response : Response { [ProtoMember(2)] public T Value; public override string ToString() { return typeof(T).Name + ": " + Value; } } static class Program { static void Main() { Packet packet = new Response { Value = 123 }; Packet clone = Serializer.DeepClone(packet); Console.WriteLine(clone.ToString()); // should be int/123 } }