none
シリアライズについて(C++/CLI) RRS feed

  • 質問

  • お世話になります。

    シリアライズの理解にはまってしまい・・・いろいろ実験した結果を下記にまとめてみましたので
    問題あればご教授ください。


    以下は、基本クラスと派生クラスのシリアライズの関係

    ・[Serializable]属性だけでシリアライズを行う場合
     →基本クラスにも[Serializable]属性を付加する必要がある。

    ・基本クラスに[Serializable]属性が付加できない場合
    (※たとえばButtonクラスなど)
     →派生クラスにISerializableインターフェイスを継承させる。
      また、ISerializationSurrogateを継承したクラスを作成し
      Formatterに代理のシリアライズするインスタンスを渡す。
      ※インスタンスは、ISerializationSurrogateを継承したクラスのインスタンス

    ・基本クラスに[Serializable]属性を付加せずにシリアライズを行う方法、
     →基本クラスは、ISerializableを継承したクラスであること
      派生側のクラスには[Serializable]属性を付加する
     
      派生クラス側の実装
      ・GetObjectData()関数内では、基本クラスのGetObjectData()関数をコールする
      ・デシリアライズ用コンストラクタも基本コンストラクタをコールする

    自分なりにまとめましたので何かご意見やまた参考となる資料等があればご教授ください。

    上記まとめた中で、シリアライズのサロゲートを実装したとき、関数が呼ばれることがありませんでした。
    何のためにサロゲートクラスを実装するのかがわかりませんでした。

    下記は、サロゲート実装したときのプログラムです。コンソールアプリケーションです。

    // SerializeSurrogateButton.cpp : メイン プロジェクト ファイルです。

    #include "stdafx.h"

    using namespace System;
    using namespace System::Windows::Forms;
    using namespace System::Collections::Generic;
    using namespace System::IO;
    using namespace System::Runtime::Serialization::Formatters::Binary;
    using namespace System::Runtime::Serialization;

    /********************************************************/
    // このプログラムは、Buttonクラスに[Serializable]属性がないため
    // シリアライズで例外が発生することに対する対策の1つです。
    /********************************************************/

    ref class ButtonSerializeSurrogate : ISerializationSurrogate
    {
    public:
     virtual void GetObjectData (Object^ obj, SerializationInfo^ info, StreamingContext context)=ISerializationSurrogate::GetObjectData
     {
      Console::WriteLine("@@@@@@ GetObjectData @@@@@");
     }
     virtual Object^ SetObjectData (Object^ obj, SerializationInfo^ info, StreamingContext context, ISurrogateSelector^ selector)=ISerializationSurrogate::SetObjectData
     {
      Console::WriteLine("@@@@@@ SetObjectData @@@@@");
      return nullptr;
     }
    };

     

    [Serializable]
    ref class ButtonEx : public Button
                       , public ISerializable
         
    {
    public:
     ButtonEx(int i)
     {
      _value = i;
     }

     // デシリアライズコンストラクタ
     ButtonEx(SerializationInfo^ info, StreamingContext context)
     {
      _value = safe_cast<int>(info->GetValue("_value",int::typeid));
     }
     // シリアライズ関数
     virtual void GetObjectData (SerializationInfo^ info, StreamingContext context) = ISerializable::GetObjectData
     {
      info->AddValue("_value",_value);
     }

     int _value;
    };

    int main(array<System::String ^> ^args)
    {
     List<Button^>^ lst = gcnew List<Button^>();
     lst->Add(gcnew ButtonEx(10));
     lst->Add(gcnew ButtonEx(20));
     lst->Add(gcnew ButtonEx(30));

     BinaryFormatter^ bf = gcnew BinaryFormatter();
     String^ filename="c:\\temp\\SerializeButtonEx.bin";
     FileStream^ fs;
     try
     {
      fs = gcnew FileStream(filename,FileMode::Create,FileAccess::Write);


         SurrogateSelector^ ss = gcnew SurrogateSelector();
      ss->AddSurrogate(Button::typeid,
       StreamingContext(StreamingContextStates::All),
       FormatterServices::GetSurrogateForCyclicalReference(gcnew ButtonSerializeSurrogate()));
      bf->SurrogateSelector=ss;

      bf->Serialize(fs,lst);  
     }
     finally
     {
      fs->Close();
     }
        return 0;
    }

     

     

    2010年12月9日 5:21

回答

  • 上記まとめた中で、シリアライズのサロゲートを実装したとき、関数が呼ばれることがありませんでした。
    何のためにサロゲートクラスを実装するのかがわかりませんでした。

    Button 型に対して登録しているのに、シリアライズしているのは List<Button^> 型だからでは?
    試しに、Button::typeid を lst->GetType() に置き換えてみれば呼び出されるかと思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年12月29日 1:28
    2010年12月9日 14:07
    モデレータ
  • >基本クラスに[Serializable]属性が付加できない場合

    >基本クラスに[Serializable]属性を付加せずにシリアライズを行う方法、

    以下はその場合におけるかなり単純なSerializeの例です。

    ref class CMyButton : public Button
    {
    public:
    	CMyButton() : Button()
    	{
    
    	}
    };
    
    ref class CMySerializationSurrogate : public ISerializationSurrogate
    {
    public:
    	CMySerializationSurrogate()
    	{
    
    	}
    
    	virtual void GetObjectData(Object^ obj,SerializationInfo^ info,StreamingContext context)
    	{
    		Console::WriteLine("ISerializationSurrogate::GetObjectData");
    	}
    
    	virtual Object^ SetObjectData(Object^ obj,SerializationInfo^ info,StreamingContext context,ISurrogateSelector^ selector)
    	{
    		Console::WriteLine("ISerializationSurrogate::SetObjectData");
    		return obj;
    	}
    };
    
    int main(array<System::String ^> ^args)
    {
    	try
    	{
    		List<CMyButton^> list;
    		BinaryFormatter binaryFormatter;
    		MemoryStream memoryStream;
    		SurrogateSelector surrogateSelector;
    		CMySerializationSurrogate mySerializationSurrogate;
    
    		list.Add(gcnew CMyButton);
    		surrogateSelector.AddSurrogate(CMyButton::typeid,StreamingContext(StreamingContextStates::All),%mySerializationSurrogate);
    		binaryFormatter.SurrogateSelector = %surrogateSelector;
    
    		binaryFormatter.Serialize(%memoryStream,%list);
    	}
    	catch(...)
    	{
    		Console::WriteLine("Error !");
    	}
    }
    

    CMySerializationSurrogateの実装は何も行っていないため、DeserializeはErrorになります。

     

    >シリアライズのサロゲートを実装したとき、関数が呼ばれることがありませんでした。

    Button::typeidをButtonEx::typeidに変えてみてください。

     

    >何のためにサロゲートクラスを実装するのかがわかりませんでした。

    [Serializable]に対応していないClassをSerialize/Deserializeするための仕組みです。サロゲート(Surrogate)は"代理の"・"代理人"という意味です。肩代わりしてくれる、ということですね。

     

    C++/CLIの書籍を探すのは大変でしょうから、C#の書籍を用意されるといいのかな、と思います。多くは書き方の違いだけですし、C#からC++/CLIの置き換えはそんなに大変ではないと思います。

    • 回答としてマーク 山本春海 2010年12月29日 1:28
    2010年12月9日 15:38

すべての返信

  • 上記まとめた中で、シリアライズのサロゲートを実装したとき、関数が呼ばれることがありませんでした。
    何のためにサロゲートクラスを実装するのかがわかりませんでした。

    Button 型に対して登録しているのに、シリアライズしているのは List<Button^> 型だからでは?
    試しに、Button::typeid を lst->GetType() に置き換えてみれば呼び出されるかと思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク 山本春海 2010年12月29日 1:28
    2010年12月9日 14:07
    モデレータ
  • >基本クラスに[Serializable]属性が付加できない場合

    >基本クラスに[Serializable]属性を付加せずにシリアライズを行う方法、

    以下はその場合におけるかなり単純なSerializeの例です。

    ref class CMyButton : public Button
    {
    public:
    	CMyButton() : Button()
    	{
    
    	}
    };
    
    ref class CMySerializationSurrogate : public ISerializationSurrogate
    {
    public:
    	CMySerializationSurrogate()
    	{
    
    	}
    
    	virtual void GetObjectData(Object^ obj,SerializationInfo^ info,StreamingContext context)
    	{
    		Console::WriteLine("ISerializationSurrogate::GetObjectData");
    	}
    
    	virtual Object^ SetObjectData(Object^ obj,SerializationInfo^ info,StreamingContext context,ISurrogateSelector^ selector)
    	{
    		Console::WriteLine("ISerializationSurrogate::SetObjectData");
    		return obj;
    	}
    };
    
    int main(array<System::String ^> ^args)
    {
    	try
    	{
    		List<CMyButton^> list;
    		BinaryFormatter binaryFormatter;
    		MemoryStream memoryStream;
    		SurrogateSelector surrogateSelector;
    		CMySerializationSurrogate mySerializationSurrogate;
    
    		list.Add(gcnew CMyButton);
    		surrogateSelector.AddSurrogate(CMyButton::typeid,StreamingContext(StreamingContextStates::All),%mySerializationSurrogate);
    		binaryFormatter.SurrogateSelector = %surrogateSelector;
    
    		binaryFormatter.Serialize(%memoryStream,%list);
    	}
    	catch(...)
    	{
    		Console::WriteLine("Error !");
    	}
    }
    

    CMySerializationSurrogateの実装は何も行っていないため、DeserializeはErrorになります。

     

    >シリアライズのサロゲートを実装したとき、関数が呼ばれることがありませんでした。

    Button::typeidをButtonEx::typeidに変えてみてください。

     

    >何のためにサロゲートクラスを実装するのかがわかりませんでした。

    [Serializable]に対応していないClassをSerialize/Deserializeするための仕組みです。サロゲート(Surrogate)は"代理の"・"代理人"という意味です。肩代わりしてくれる、ということですね。

     

    C++/CLIの書籍を探すのは大変でしょうから、C#の書籍を用意されるといいのかな、と思います。多くは書き方の違いだけですし、C#からC++/CLIの置き換えはそんなに大変ではないと思います。

    • 回答としてマーク 山本春海 2010年12月29日 1:28
    2010年12月9日 15:38
  • Button::typeidをButtonEx::typeidに変えてみてください。

    それと、List<Button^> を List<ButtonEx^> に変える必要がありそうです。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年12月9日 22:03
    モデレータ
  • ご返事ありがとうございます。

     

    Button 型に対して登録しているのに、シリアライズしているのは List<Button^> 型だからでは?
    試しに、Button::typeid を lst->GetType() に置き換えてみれば呼び出されるかと思います。

    確かに、コールされました。 また第一引数のObjectもList<Button^>型で確認できました。
    以下にまとめてみました。

    Button::typeid (コールされる)  ButtonEx::GetDataObject関数
               (コールされない) サロゲート::GetDataObject関数

    lst->GetType() (コールされない) ButtonEx::GetDataObject関数
                (コールされる)  サロゲート::GetDataObject関数

    シリアライズ対象のオブジェクトのサロゲート登録ということなので、上記の結果は理解はできました。
    また意味が理解できました。

    しかし一つ理解できなことがあります。

    Button::typeidのサロゲートは何も意味を持たない気がしますが、
    これを登録することで、シリアライズ時に例外が発生しなかったのはなぜでしょうか。

     

    2010年12月10日 5:09