ISerializableなクラスをシリアライズして、別のISerializableなクラスにデシリアライズ(型変換)
SerializationBinderクラスを継承してIFormatter.Binderをカスタマイズすると、デシリアライズ時の変換先の型を設定できるようです(*1)。つまりは一種の型変換です。でわ早速、「3次元ベクトルクラスのz要素を切り落として2次元ベクトルクラスに変換」を、BinaryFormatterによるシリアライズ経由で試してみます(*2)。
まずISerializableな2Dベクトル
[System.Serializable] public class MyVector2 : System.Runtime.Serialization.ISerializable { public float x { get { return m_x; } set { m_x = value; } } public float y { get { return m_y; } set { m_y = value; } } public MyVector2() { m_x = .0f; m_y = .0f; } public MyVector2(float _x, float _y) { m_x = _x; m_y = _y; } // デシリアライズコンストラクタ protected MyVector2(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext stream) { try { m_x = info.GetSingle("x"); } catch (System.Exception) { m_x = .0f; } try { m_y = info.GetSingle("y"); } catch (System.Exception) { m_y = .0f; } } // シリアライズメソッド public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { info.AddValue("x", m_x); info.AddValue("y", m_y); } public override System.String ToString() { return System.String.Format("({0:0.000}, {1:0.000})", m_x, m_y); } private float m_x, m_y; };
で、z要素を増やしただけの3Dバージョン。
[System.Serializable] public class MyVector3 : System.Runtime.Serialization.ISerializable { public float x { get { return m_x; } set { m_x = value; } } public float y { get { return m_y; } set { m_y = value; } } public float z { get { return m_z; } set { m_z = value; } } public MyVector3() { m_x = .0f; m_y = .0f; m_z = .0f; } public MyVector3(float _x, float _y, float _z) { m_x = _x; m_y = _y; m_z = _z; } // デシリアライズコンストラクタ protected MyVector3( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext stream) { try { m_x = info.GetSingle("x"); } catch (System.Exception) { m_x = .0f; } try { m_y = info.GetSingle("y"); } catch (System.Exception) { m_y = .0f; } try { m_z = info.GetSingle("z"); } catch (System.Exception) { m_z = .0f; } } // シリアライズメソッド public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { info.AddValue("x", m_x); info.AddValue("y", m_y); info.AddValue("z", m_z); } public override System.String ToString() { return System.String.Format("({0:0.000}, {1:0.000}, {2:0.000})", m_x, m_y, m_z); } private float m_x, m_y, m_z; };
無理やりキャストするためのSerializationBinderを定義。
public class TypeCastSerializeBinder : System.Runtime.Serialization.SerializationBinder { // コンストラクタにキャスト先の型を指定すると、是が非でもその型へのデシリアライズをトライするようになる。 public TypeCastSerializeBinder(System.Type castToType) { m_t = castToType; } public override System.Type BindToType(System.String assemblyName, System.String typeName) { return m_t; } private System.Type m_t; };
3Dクラスをシリアライズして、そのStreamデータから2Dクラスにデシリアライズ。
class MainClass { static void Main(string[] args) { MyVector3 vecA = new MyVector3(1.0f, 1.0f, 1.0f); MyVector2 vecB = new MyVector2(-1.0f, -1.0f); System.Console.WriteLine("vecA:" + vecA.ToString()); System.Console.WriteLine("vecB:" + vecB.ToString()); using (System.IO.Stream stream = new System.IO.MemoryStream()) { // serialize System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serialize_formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); serialize_formatter.Serialize(stream, vecA); // stream seek stream.Position = 0; // deserialize System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserialize_formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); // ↓ここでBinderを設定! deserialize_formatter.Binder = new TypeCastSerializeBinder( typeof(MyVector2) ); vecB = deserialize_formatter.Deserialize(stream) as MyVector2; } System.Console.WriteLine("vecB(deserialize from vecA):" + vecB.ToString()); System.Console.ReadKey(); } };
出力結果
vecA:(1.000, 1.000, 1.000) vecB:(-1.000, -1.000) vecB(deserialize from vecA):(1.000, 1.000)
この様式だと、デシリアライズ時のコンストラクトを好きに処理できるので、変換失敗時の処理を好きにしやすいのが便利といえば便利かもしれません。それに、SerializationBinderの継承のところ、もうちょっとダイナミックなアセンブリからの型検索にも使える雰囲気です。