【问题标题】:Generic BinaryReader通用 BinaryReader
【发布时间】:2012-03-27 03:49:27
【问题描述】:

我正在编写二进制序列化器/解串器来将多种对象类型转换为字节流/从字节流转换。这些对象代表通过蓝牙或 USB 连接的设备的 API 命令及其相关响应。我正在使用BinaryWriterBinaryReader 向/从流中写入/读取。

序列化器很简单。要序列化的属性用一个属性标记,该属性指定将它们写入字节流的顺序。我使用反射和重载解析来遍历属性,并选择BinaryWriter 的正确Write(...) 方法。

反序列化器并不是那么简单。再次,我可以遍历特定响应类中的属性,我期望确定需要从流中读取的类型。棘手的一点是选择正确的方法来调用BinaryReader 来读取我需要的值。我想到了两种方法。

  1. 一个大的 switch 语句,根据要读取的类型调用正确的 ReadXXXX() 方法。
  2. 使用我需要的类型名在字符串中构建我需要的方法名,然后使用reelection调用方法。

有没有我没有想到的更简单的方法?遗憾的是,您不能根据所需的返回类型进行重载解析。

【问题讨论】:

    标签: c# .net binary deserialization


    【解决方案1】:

    我在二进制反序列化器中使用了选项 1(大 switch 语句)。更简洁的方法可能是这样的:

    {
        object result;
    
        BinaryReader ...;
        foreach (var propertyInfo in ...)
        {
            Func<BinaryReader, object> deserializer;
            if (!supportedTypes.TryGetValue(propertyInfo.PropertyType, out deserializer))
            {
                throw new NotSupportedException(string.Format(
                    "Type of property '{0}' isn't supported ({1}).", propertyInfo.Name, propertyInfo.PropertyType));
            }
    
            var deserialized = deserializer(reader);
            propertyInfo.SetValue(result, deserialized, null);
        }
    }
    
    private static Dictionary<Type, Func<BinaryReader, object>> supportedTypes = new Dictionary<Type, Func<BinaryReader, object>>
    {
        { typeof(int), br => br.ReadInt32() },
        // etc
    };
    

    另一种选择是让命令类自己进行序列化:

    interface IBinarySerializable
    {
        void Serialize(BinaryWriter toStream);
        void Deserialize(BinaryReader fromStream);
    }
    

    然后在你的命令中:

    abstract class Command : IBinarySerializable
    {
    
    }
    
    class SomeCommand : Command
    {
        public int Arg1 { get; set; }
    
        public void Serialize(BinaryWriter toStream)
        {
            toStream.Write(Arg1);
        }
    
        public void Deserialize(BinaryReader fromStream)
        {
            Arg1 = fromStream.ReadInt32();
        }
    }
    

    以及通用的序列化方法:

    void Serialize<T>(T obj) where T : IBinarySerializable
    {
        obj.Serialize(_stream);
    }
    
    T Deserialize<T>() where T : new(), IBinarySerializable
    {
        var result = new T();
        result.Deserialize(_stream);
        return result;
    }
    

    但是这样你最终可能会复制一些代码。 (另一方面,派生类可以调用其父类版本的 Serialize/Deserialize,如果这在您的场景中有意义,并且效果很好。)

    【讨论】:

    • 几个非常好的选择,谢谢。我倾向于第一个,因为它更适合我当前的代码。感谢您的帮助。
    【解决方案2】:

    我不知道我是否完全理解你想要做什么。但听起来您应该仔细看看 BinaryFormatter 及其在 .NET 中的序列化代理。 BinaryFormatter 让您可以轻松地序列化和反序列化对象,而序列化代理让您可以添加自定义序列化和反序列化逻辑,还可以将一个对象重新映射到另一个对象。

    看这里:

    二进制格式化程序 http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx

    ISerializationSurrogate http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializationsurrogate.aspx

    代理选择器 http://msdn.microsoft.com/en-us/library/system.runtime.serialization.surrogateselector.aspx

    序列化绑定器 http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder.aspx

    您还可以在此处看到一个小示例,其中我有一个可以将任何对象序列化为 base64 编码字符串的方法,然后是一个可以反序列化此字符串的反序列化方法。我还在格式化程序中添加了一个 SerializationBinder,它将 MyOldClass 类型的序列化重新映射到 MyNewClass 类型,还添加了一个自定义 ISerializationSurrogate,它可以在将对象中的字段值添加到 ner 类之前对其进行处理。

    public class SerializeDeserializeExample {
       public string Serialize(object objectToSerialize) {
          using(var stream = new MemoryStream()) {
             new BinaryFormatter().Serialize(stream, objectToSerialize);
             return Convert.ToBase64String(stream.ToArray());
          }
       }
    
       public object Deserialize(string base64String) {
          using(var stream = new MemoryStream(Convert.FromBase64String(base64String))) {
             var formatter = new BinaryFormatter();
             var surrogateSelector = new SurrogateSelector();
             formatter.SurrogateSelector = surrogateSelector;
             formatter.Binder = new DeserializationBinder(surrogateSelector);
             return formatter.Deserialize(stream);
          }
       }
    }
    
    
    public class MyDeserializationBinder : SerializationBinder {
       private readonly SurrogateSelector surrogateSelector;
    
       public MyDeserializationBinder(SurrogateSelector surrogateSelector) {
          this.surrogateSelector = surrogateSelector;
       }
    
       public override Type BindToType(string assemblyName, string typeName) {
          if(typeName.Equals("MyOldClass", StringComparison.InvariantCultureIgnoreCase)) {
             return RemapToType();
          }
          return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
       }
    
       private Type RemapToType() {
          var remapToType = typeof(MyNewClass);
          surrogateSelector.AddSurrogate(remapToType,
                                  new StreamingContext(StreamingContextStates.All),
                                  new MyCustomDeserializationSurrogate());
          return remapToType;
       }
    }
    
    public sealed class MyCustomDeserializationSurrogate : ISerializationSurrogate {
    
       public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) {
          throw new NotImplementedException();
       }
    
       public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
          var objectType = obj.GetType();
          var fields = GetFields(objectType);
          foreach(var fieldInfo in fields) {
             var fieldValue = info.GetValue(fieldInfo.Name, fieldInfo.FieldType);
             fieldValue = DoSomeProcessing(fieldValue);
             fieldInfo.SetValue(obj, fieldValue);
          }
          return obj;
       }
    
       private static IEnumerable<FieldInfo> GetFields(Type objectType) {
          return objectType.GetFields(BindingFlags.Instance | BindingFlags.DeclaredOnly |
                               BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
       }
    
       private static object DoSomeProcessing(object value){
          //Do some processing with the object
       }
    }
    

    【讨论】:

    • 看起来是个不错的选择。不幸的是,这些类在 .Net 中不适用于 Metro Style 应用程序,这是我的目标平台。我可能应该为我的问题添加一个标签。
    猜你喜欢
    • 2012-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多