.NET ROOM.NET ROOMトップへCOMRADE総合トップへ
「4:オブジェクトのシリアライズ」へ

Staticフィールドのシリアライズ
問題点2の解決方法


 次に、問題点2を克服するため、Staticフィールドをシリアライズする方法を説明します。
リスト6のようにSerializableClassを修正した後に、リスト4のコードで確認すると、
期待通りにStaticなインスタンスがシリアライズ/デシリアライズされていることを確認できます。
なお、このコードは、付録のStaticFieldSerializeTestディレクトリに収録しています。

リスト6:Staticフィールドとプロパティを追加したSerializableClass
[Serializable]
public class SerializableClass : ISerializable {
 [Serializable]
 class StaticFieldSerializationHelper : IObjectReference {
  object IObjectReference.GetRealObject(
   StreamingContext context) { return static_field; }
 }
 static readonly SerializableClass static_field =
  new SerializableClass(1);
 int data;
 public SerializableClass(int data) { Data = data; }
 SerializableClass(SerializationInfo info,
            StreamingContext context) {
  data = (int)info.GetValue("data", typeof(int));
 }

 void ISerializable.GetObjectData(SerializationInfo info,
                     StreamingContext context) {
  if (this == static_field)
   info.SetType(typeof(StaticFieldSerializationHelper));
  else
   info.AddValue("data", data);
 }
 public int Data {
  get { return data; }
  set { data = value; }
 }
 public static SerializableClass StaticProperty {
  get { return static_field; }
 }
}


 ここでポイントとなるのは、SerializationInfoのSetTypeメソッドとIObjectReferenceインターフェイスです。
リスト6のコードのように、ISerializable.GetObjectDataメソッドの中で、
現在シリアライズしているインスタンスがStaticなインスタンスであるかどうかを確認したうえで、
そのインスタンス専用のデシリアライズ用クラスをSetTypeメソッドで指定します。
Staticインスタンスのデシリアライズ時には直接IObjectReference.GetRealObjectによって
目的のインスタンスが返されるため、デシリアライズ用のコンストラクタは使用されません。

 また、このサンプルではひとつのStaticフィールドのみを扱っていますが、
Staticフィールドが複数存在する場合も考えられます。
現在の.NET Frameworkでは、IObjectReferenceメソッドの引数の値が無意味なので、
残念ながらそれぞれのStaticフィールドごとにデシリアライズ用のクラスを用意する必要があるようです。
将来のバージョンでは、ひょっとすると引数のStreamingContextに有用な情報が入っていて、
それによって返す値を変更することができるようになるかもしれません。

 複数のStaticフィールドがある場合のコード例は、
付録のMultipleStaticFieldsTestディレクトリに収録しています。
Staticなフィールドがあり、さらにStaticではないインスタンスがあるクラスの場合は、リスト7のように
ISerializable.GetObjectDataメソッドを実装します。

リスト7:ISerializable.GetObjectDataメソッドの実装
void ISerializable.GetObjectData(SerializationInfo info,
                    StreamingContext context) {
 if (this == static_field1)
  info.SetType(typeof(StaticField1SerializationHelper));
 else if (this == static_field2)
  info.SetType(typeof(StaticField2SerializationHelper));
 else
  info.AddValue("data", data);
}


  SerializationInfoクラスのAddValueメソッドを呼び出すのを忘れてしまうと、
Staticではないインスタンスのシリアライズに失敗してしまうので、注意する必要があります。


ページトップへ


シリアライズ可能でないクラスから派生クラスを作成
問題点3の解決方法


 問題点3の解決方法はいくつかあります。

 ひとつ目は、ISerializationSurrogateインターフェイスを定義し、
FormatterのSurrogateSelectorプロパティに指定する方法です。
このインターフェイスおよびSurrogateSelectorプロパティに関する詳細は、リファレンスを参照してください。

 2つ目は、派生クラスから取得可能なフィールド(PublicおよびProtectedなフィールド)を、
ISerializableのGetObjectDataメソッド内で指定するという方法です。
この方法は、すでに説明したように、“SerializationInfoのAddValueメソッドに
任意の名前を使用できる”という機能を使用します。

 たとえば、Controlなどはシリアライズ可能ではないクラスです。
そのため、Controlから派生する自作のコントロールをシリアライズ可能にするためには、
上記のどちらかの方法をとる必要があります。

 ただし、Controlがシリアライズ可能になっていないのに は理由があります。
詳細は次回に説明しますが、コントロ ールをVS.NET上に配置して使用した場合、
コードの生成 によって設定値が永続化されるため、保持しているデータ をシリアライズする必要はあっても、
外見に関する設定は シリアライズする必要がありません。

  シリアライズ可能でないクラスから派生したクラスを、 本当にシリアライズ可能にする必要があるかどうか、
よく検討する必要があるでしょう。


ページトップへ


最後に

 今回はデータの永続化を理解するための前準備として、
オブジェクトのシリアライズに関して説明しました。
次回はいよいよVS.NETでの永続化に関して「コントロールのデータの永続化」という
観点から説明してゆきます。


前へ | ページトップへ | 次へ