3

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

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

包 Kannon.State;
消息状态定义 {
    枚举状态类型 {
        图形 = 0;
        音频 = 1;
        头脑 = 2;
        物理 = 3;
        网络 = 4;
        通用 = 5;
    }
    重复的 StateTypes requiredStates = 1;
    可选 GraphicsStateDef 图形 = 2;
    可选的 AudioStateDef 音频 = 3;
         (ETC)
}

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

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

我的目标是允许这些 _StateDef 消息稍后使用它需要的字段进行扩展。但是,此扩展将独立于我当前正在编写的库发生。

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

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

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

我设计的一个解决方案,看起来很老套,是在引用 DLL 中定义一个带有扩展方法的类,如下所示:

    公共静态类 GraphicsExt
    {
        枚举字段
        {
            某个值 = 1,
            一些其他值 = 2
        }

        公共静态 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 成为流的容器,并且只为“repeated StateTypes requiredStates=1”字段提取足够的信息。然后,在引用程序集中,可以将流的其余部分反序列化为相应的状态。

有没有人对如何解决这个问题有建议?一些想法正在形成,但没有具体的想法,我喜欢其他人的意见。

4

2 回答 2

1

我是 protobuf-net 的作者。我没有添加任何内容来解决直接呈现的场景(Extensible代码除外),但我愿意就您认为该做什么提出建议。

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

于 2009-08-15T21:46:09.947 回答
0

最终答案:

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

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

namespace Kannon.State
{
    /// <summary>
    /// ReferenceDefinition describes the layout of the reference in general.
    /// It tells what states it should have, and stores the stream buffers for later serialization.
    /// </summary>
    [ProtoBuf.ProtoContract]
    public class ReferenceDefinition
    {
        /// <summary>
        /// There are several built in state types, as well as rudimentary support for a "Generic" state.
        /// </summary>
        public enum StateType
        {
            Graphics=0,
            Audio,
            Mind,
            Physics,
            Network,
            Generic
        }

        /// <summary>
        /// Represents what states should be present in the ReferenceDefinition
        /// </summary>
        [ProtoBuf.ProtoMember(1)]
        List<StateType> m_StatesPresent = new List<StateType>();

        /// <summary>
        /// Represent a list of StateDefinitions, which hold the buffers for each different type of state.
        /// </summary>
        [ProtoBuf.ProtoMember(2)]
        List<StateDefinition> m_StateDefinition = new List<StateDefinition>();

        /// <summary>
        /// Add a state, mapped to a type, to this reference definition.
        /// </summary>
        /// <param name="type">Type of state to add</param>
        /// <param name="def">State definition to add.</param>
        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);
        }
    }

    /// <summary>
    /// Represents a definition of some gamestate, storing protobuffered data to be remapped to the state.
    /// </summary>
    [ProtoBuf.ProtoContract]
    public class StateDefinition
    {
        /// <summary>
        /// Name of the state
        /// </summary>
        [ProtoBuf.ProtoMember(1)]
        string m_StateName;
        /// <summary>
        /// Byte array to store the "data" for later serialization.
        /// </summary>
        [ProtoBuf.ProtoMember(2)]
        byte[] m_Buffer;

        /// <summary>
        /// Constructor for the state definition, protected to enforce the Pack and Unpack functionality to keep things safe.
        /// </summary>
        /// <param name="name">Name of the state type.</param>
        /// <param name="buff">byte buffer to build state off of</param>
        protected StateDefinition(String name, byte[] buff)
        {
            m_StateName = name;
            m_Buffer = buff;
        }

        /// <summary>
        /// Unpack a StateDefinition into a GameState
        /// </summary>
        /// <typeparam name="T">Gamestate type to unpack into.  Must define Protobuf Contracts.</typeparam>
        /// <param name="def">State Definition to unpack.</param>
        /// <returns>The unpacked state data.</returns>
        public static T Unpack<T>(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<T>(new MemoryStream(def.m_Buffer));
            else
                // Otherwise, return the equivalent of Null.
                return default(T);
        }

        /// <summary>
        /// Pack a state type into a State Definition
        /// </summary>
        /// <typeparam name="T">Gamestate to package up.  Upst define protobuf contracts.</typeparam>
        /// <param name="state">State to pack up.</param>
        /// <returns>A state definition serialized from the passed in state.</returns>
        public static StateDefinition Pack<T>(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<T>(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());
            }
        }
    }
}
于 2009-08-20T17:33:29.010 回答