2012-03-27 6 views
5

私はバイナリserialiser/deserialserを書いて、多くのオブジェクトタイプをバイトストリームとの間で変換しています。これらのオブジェクトは、Bluetoothコマンドや、BluetoothやUSBで接続されたデバイスの応答を表します。私はBinaryWriter & BinaryReaderを使用してストリームへの書き込み/読み出しを行っています。一般BinaryReader

シリアライザは簡単です。直列化されるべきプロパティは、それらがバイトストリームに書き込まれる順序を指定する属性でタグ付けされる。反射とオーバーロードの解像度処理を使用してプロパティを反復処理して、BinaryWriterの正しいWrite(...)メソッドを選択します。

デシリアライザはあまり簡単ではありません。繰り返しますが、ストリームから読み込む必要がある型を特定することを期待している特定の応答クラスのプロパティを繰り返し処理できます。微妙なビットは、私が必要とする値を読むためにBinaryReaderを呼び出す正しい方法を選んでいます。私は2つのアプローチを考えました。

  1. 読み取るタイプに基づいて正しいReadXXXX()メソッドを呼び出す大きなswitch文。
  2. 文字列に必要なメソッドの名前を作成し、relectionを使用してメソッドを呼び出すために必要な型の名前を使用します。

私は考えていない簡単な方法はありますか?あなたが望む戻り値のタイプに基づいてオーバーロード解決を行うことはできません。

答えて

1

バイナリデシリアライザでオプション1(big switch文)を使用しました。あなたのコマンドで次に

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(); 
    } 
} 

と汎用シリアル化

{ 
    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 
}; 

別のオプションは、コマンドクラス自体が直列化を行うようにすることです:クリーナー方法は何かのようにすることができ方法:

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

本当に良いオプションのカップル、ありがとう。最初のコードに向かって傾いているのは、それが現在のコードとうまく適合しているからです。ご協力いただきありがとうございます。 –

0

あなたがしようとしていることを完全に理解しているかどうかわかりません。しかし、.NETのBinaryFormatterとそのシリアライゼーションサロゲートを詳しく見てみると大変です。 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でエンコードされた文字列にシリアル化できるメソッドがあり、次にこの文字列を逆シリアル化できるdeserializeメソッドがあります。また、MyataClass型のシリアル化をMyNewClass型に再マッピングするフォーマッタにSerializationBinderを追加し、nerクラスに追加する前にオブジェクトのフィールドの値を処理できるカスタムISerializationSurrogateを追加します。

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 
    } 
} 
+0

良いオプションのように見えます。残念ながら、これらのクラスはターゲットプラットフォームであるMetroスタイルアプリ用の.Netでは使用できません。私はおそらく私の質問にそれのためのタグを追加する必要があります。 –