我正在写一个网络应用程序。为了提高速度和效率,我决定使用简单的长度前缀的序列化消息。我调查了几个,即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
。服务器每秒处理许多消息(希望!)。
我觉得我错过了一些东西......我意识到,完全没有元数据,这些消息需要在线路的两端都有描述符。这不是问题,我将完全控制两端的代码。
我如何处理此问题非常?
答案 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();