Универсальный BinaryReader - PullRequest
6 голосов
/ 27 марта 2012

Я пишу двоичный сериализатор / десериализатор для преобразования ряда типов объектов в / из байтового потока. Объекты представляют команды API и связанные с ними ответы для устройства, подключенного через Bluetooth или USB. Я использую BinaryWriter & BinaryReader для записи / чтения в / из потока.

Сериализатор прост. Свойства, которые должны быть сериализованы, помечены атрибутом, который указывает порядок, в котором они должны быть записаны в поток байтов. Я перебираю свойства, используя ручки разрешения отражения и перегрузки, выбирая правильный Write(...) метод BinaryWriter.

Десериализатор не так прост. Опять же, я могу перебирать свойства в конкретном классе ответа, который, как я ожидаю, определит типы, которые должны быть прочитаны из потока. Сложный бит выбирает правильный метод для вызова на BinaryReader, чтобы прочитать значение, которое мне нужно. Я думал о двух подходах.

  1. Большой оператор switch, который вызывает правильный метод ReadXXXX(), основанный на типе, который нужно прочитать.
  2. Используйте имя типа, которое мне нужно, чтобы построить имя метода, который мне нужен, в строке, а затем вызовите метод, используя relection.

Есть ли более простой способ, о котором я не думаю? Жаль, что вы не можете сделать разрешение перегрузки в зависимости от типа возвращаемого значения.

Ответы [ 2 ]

1 голос
/ 27 марта 2012

Я использовал вариант 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, если это имеет смысл в вашем сценарии, и это хорошо работает.)

0 голосов
/ 27 марта 2012

Я не знаю, полностью ли я понимаю, что вы пытаетесь сделать.Но это звучит так, как будто вы познакомитесь с BinaryFormatter и его суррогатами сериализации в .NET.BinaryFormatter позволяет легко сериализовать и десериализовывать объекты, а суррогаты сериализации позволяют добавлять свою собственную логику сериализации и десериализации, а также позволяют переназначить один объект в другой.

Посмотрите здесь:

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

SurrogateSelector http://msdn.microsoft.com/en-us/library/system.runtime.serialization.surrogateselector.aspx

SerializationBinder 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
   }
}
...