具有扩展的协议缓冲区

我可能忽略了一些东西,但我正在尝试将协议缓冲区转换为一种简单的方法,以便稍后提供扩展。 这似乎有点不清楚,所以我会直接跳到这个问题。

我正在编写一个程序集来支持各种任务,其中一个任务包括描述结构化数据。 使用协议缓冲区的最佳时机。 使用协议缓冲区的主类称为StateDefinition。 这是我为它提出的.proto文件:

包Kannon.State;
消息StateDefinition {
    枚举StateTypes {
         GRAPHICS = 0;
         AUDIO = 1;
         MIND = 2;
        物理= 3;
         NETWORK = 4;
         GENERIC = 5;
     }
    重复StateTypes requiredStates = 1; 
    可选GraphicsStateDef Graphics = 2;
    可选AudioStateDef Audio = 3;
          (等等)
 }

消息GraphicsStateDef {
    扩展100到最大;
 }

消息AudioStateDef {
    扩展100到最大;
 }
     (等等)

我的目标是允许稍后使用所需的字段扩展那些_StateDef消息。 但是,此扩展将独立于我正在编写的库。

Kagents.dll – >处理StateDefinition解析等。

引用Kagents.dll的东西 – >有一个带有“extend GraphicsStateDef”的protobuff文件来定义所需的状态。

我希望定义“extend GraphicsStateDef”会生成允许我使用属性来访问这些字段的代码,并避免使用繁琐的“Extendible.AppendValue()”和GetValue()语法。

我设计的一个似乎是hackish的解决方案是使用扩展方法在引用DLL中定义一个类,如下所示:

     public static class GraphicsExt
     {
        枚举字段
         {
             someValue = 1,
             someOtherValue = 2
         }

         public static Int32 someValue(this State.GraphicsStateDef def)
         {
             return Extensible.GetValue(def,Fields.someValue);
         }
         public static void someValue(this State.graphicsStateDef def,Int32 value)
         {
             Extensible.AppendValue(def,fields.someValue,value);
         }
     }

如果有人能想出更好的方法,我会非常感激。 =)另外,我不确定我对这个问题的描述是多么清晰,所以如果我能提供任何澄清或进一步的信息,请告诉我。 =)

编辑:所以,经过思考很多,并意识到我正在接近错误的问题。 StateReference应该存储不同GameState的列表。 同样,它存储StateDefinition,它应描述此状态引用的状态。 目前,我正在尝试将状态缓冲区反序列化为不同的类(GraphicsStateDef),而我真的应该反序列化为状态对象本身。

因此,我需要重新考虑设计,使StateDefinition成为流的容器,并为“重复的StateTypes requiredStates = 1”字段提取足够的信息。 然后,在引用程序集中,可以将流的其余部分反序列化为相应的状态。

有没有人有关于如何处理此问题的建议? 一些想法正在制定,但没有具体的,我喜欢别人的意见。

我是protobuf-net的作者。 我没有添加任何东西来解决直接呈现的场景(除了Extensible代码),但我愿意接受你认为它该做什么的建议。

我还需要检查“protoc”(我在代码生成之前用来解析.proto的.proto编译器)是否允许我区分常规成员和扩展成员。

最终答案:

好吧,所以,几天前我找到了一个解决方案,我只是更新这个,以防其他人遇到同样的问题。

整个问题源于我没有意识到protobuf-net可以支持byte []。 所以,这是我的解决方案:

 namespace Kannon.State { ///  /// ReferenceDefinition describes the layout of the reference in general. /// It tells what states it should have, and stores the stream buffers for later serialization. ///  [ProtoBuf.ProtoContract] public class ReferenceDefinition { ///  /// There are several built in state types, as well as rudimentary support for a "Generic" state. ///  public enum StateType { Graphics=0, Audio, Mind, Physics, Network, Generic } ///  /// Represents what states should be present in the ReferenceDefinition ///  [ProtoBuf.ProtoMember(1)] List m_StatesPresent = new List(); ///  /// Represent a list of StateDefinitions, which hold the buffers for each different type of state. ///  [ProtoBuf.ProtoMember(2)] List m_StateDefinition = new List(); ///  /// Add a state, mapped to a type, to this reference definition. ///  /// Type of state to add /// State definition to add. public void AddState(StateType type, StateDefinition def) { // Enforce only 1 of each type, except for Generic, which can have as many as it wants. if (m_StatesPresent.Contains(type) && type != StateType.Generic) return; m_StatesPresent.Add(type); m_StateDefinition.Add(def); } } ///  /// Represents a definition of some gamestate, storing protobuffered data to be remapped to the state. ///  [ProtoBuf.ProtoContract] public class StateDefinition { ///  /// Name of the state ///  [ProtoBuf.ProtoMember(1)] string m_StateName; ///  /// Byte array to store the "data" for later serialization. ///  [ProtoBuf.ProtoMember(2)] byte[] m_Buffer; ///  /// Constructor for the state definition, protected to enforce the Pack and Unpack functionality to keep things safe. ///  /// Name of the state type. /// byte buffer to build state off of protected StateDefinition(String name, byte[] buff) { m_StateName = name; m_Buffer = buff; } ///  /// Unpack a StateDefinition into a GameState ///  /// Gamestate type to unpack into. Must define Protobuf Contracts. /// State Definition to unpack. /// The unpacked state data. public static T Unpack(StateDefinition def) where T:GameState { // Make sure we're unpacking into the right state type. if (typeof(T).Name == def.m_StateName) return ProtoBuf.Serializer.Deserialize(new MemoryStream(def.m_Buffer)); else // Otherwise, return the equivalent of Null. return default(T); } ///  /// Pack a state type into a State Definition ///  /// Gamestate to package up. Upst define protobuf contracts. /// State to pack up. /// A state definition serialized from the passed in state. public static StateDefinition Pack(T state) where T:GameState { // Using a memory stream, to make sure Garbage Collection knows what's going on. using (MemoryStream s = new MemoryStream()) { ProtoBuf.Serializer.Serialize(s, state); // Uses typeof(T).Name to do semi-enforcement of type safety. Not the best, but it works. return new StateDefinition(typeof(T).Name, s.ToArray()); } } } }