以性能意识的方式动态地将消息反序列化为各种类型

时间:2018-02-01 22:23:37

标签: c# generics serialization types protocol-buffers

我正在写一个网络应用程序。为了提高速度和效率,我决定使用简单的长度前缀的序列化消息。我调查了几个,即protobuf,但最终定居在ZeroFormatter

我的邮件如下: [[length][header][serialized message contents]]

然而,我无法弄清楚反序列化消息的优雅方式。

显然,我可以有一些基本上可以做的功能:

IMessage message;

  switch (message.Type)
  {
      case 1:
          message = new MessageOne(...)
          break;
      case 2:
          message = new MessageTwo(...)
          break;
      default:
          Console.WriteLine("Unknown message type");
          break;
  }

但我希望至少有100种不同的消息类型。这使得一个非常难看的switch

我尝试创建一个动态生成消息的方法,并在编译中学到了很多课程(不能将system.Type用作泛型类型......):

var messageTable = new Dictionary<int, Type>();

populate the dictionary with types...

public bool TryParse(byte[] buffer, out IMessage message)
{
    Type msgType = messageTable[buffer[0]];

    message = ZeroFormatterSerializer.Deserialize<msgType>(buffer.Skip(1)); <== Obviously cant do this with generics...
}

还尝试在Type变量上使用静态函数...

var messageTable = new Dictionary<int, Message>();

populate the dictionary with types...

public bool TryParse(byte[] buffer, out IMessage message)
{
    Type msgType = messageTable[buffer[0]];

    message = msgType.Serializer.Deserialize(buffer.Skip(1)); <== Obviously cant do this with static methods...
}

绝对希望避免昂贵的事情,例如Reflection。服务器每秒处理许多消息(希望!)。

我觉得我错过了一些东西......我意识到,完全没有元数据,这些消息需要在线路的两端都有描述符。这不是问题,我将完全控制两端的代码。

我如何处理此问题非常

1 个答案:

答案 0 :(得分:1)

您可以在代码开头创建动态Func,将它们存储在字典中然后使用它们。

Dictionary<byte, Func<byte[], IMessage>> converters = 
                                        new Dictionary<byte, Func<byte[], IMessage>>();

public void CreateConverter<T>(byte Id) where T : IMessage
{
    converters.Add(Id, (byte[] Data) => ZeroFormatterSerializer.Deserialize<T>(Data.Skip(1)));
}

public IMessage Deserialize(byte[] Data)
{
    return converters[data[0]](Data);
}

//Somewhere on your initialization code...
CreateConverter<0, MessageOne>();
CreateConverter<1, MessageTwo>();
//...

//Now you can simply do
IMessage msg = Deserialize(Data);

另外,我会尽可能地避免Skip部分,它是一个IEnumerable扩展而且速度很慢,如果反序列化器支持它使用ArraySegment跳过该字节,否则我会测试如果创建新数组并使用Buffer.BlockCopy比使用Skip更快。

更进一步,这可以通过以下方式扩展为全自动:

//Modify IMessage
public abstract class IMessage
{
    public abstract byte Id{ get; }
}

//Add the property to your messages
public class MessageOne : IMessage
{
    public override byte Id{ get{ return 0; } }
}

//Create a Init method

void Init()
{
    var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof(IMessage)));
    MethodInfo method = this.GetType().GetMethod("CreateConverter");

    foreach (var type in types)
    {
        var instance = (IMessage)Activator.CreateInstance(type);
        MethodInfo genericMethod = method.MakeGenericMethod(type);
        genericMethod.Invoke(this, instance.Id);
    }

}

//Now you only need to do at the begining of your code...
Init();