- PR -

CodeDomの逆シリアル化

投稿者投稿内容
JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-10-02 11:09
きくちゃん様、Jitta様 レスありがとうございます。

きくちゃん様。
引用:

ちなみに、JUNJUNさんが目指しているような初期化コードを、実際に吐くコントロールってあるんですか?


ある事を期待して今さがしています。
見つけたら報告します。

Jitta様。
引用:

 たとえば XML ファイルで、

<変数名>実態</変数名>
<プロパティ名>変数名</プロパティ名>

としても、プロパティ名は「変数名」という値が入るようなものではないでしょうか。


すみません。XMLはよくわかりません。上記が意味することはどういうことなのでしょうか?

引用:

 ってか、手段が目的になっているような気がする。
 カスタムクラスのプロパティデザイナを作る方法は、チュートリアルがありますよね。そこから、なぜ CodeDOM に飛んだんだろう?
 また、複数のコントロールを配置したとき、一時的な変数が一意であることは、どうやって保証するのでしょう?


CodeDOMの勉強が目的なのです。
一時的な変数が一意であることの保証は調査してみます。

現在の心境としては、間接的な代入である
コード:
Dim temp as New SampleClass1
MyControl1.MyProperty = temp

は無理なのかなと挫折しかけてます。
コード:
MyControl1.MyProperty = New SampleClass1("aaaa", 1, "bbb", False)

というような書き方でSampleClass1の全ての情報をコンストラクタの引数に入れる方法を検討するしかないのかな、と・・・。

今後、間接的な代入をしているコードを探してみて、なければ上記の方法を採用しようと思います。
でも、多分あると思うんですよね・・・
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-10-02 17:03
引用:

JUNJUNさんの書き込み (2005-10-02 11:09) より:


引用:

 たとえば XML ファイルで、

<変数名>実態</変数名>
<プロパティ名>変数名</プロパティ名>

としても、プロパティ名は「変数名」という値が入るようなものではないでしょうか。


すみません。XMLはよくわかりません。上記が意味することはどういうことなのでしょうか?


これは、
引用:

現在の心境としては、間接的な代入である
コード:
Dim temp as New SampleClass1
MyControl1.MyProperty = temp

は無理なのかなと挫折しかけてます。


こういうことです。

 代入する(参照させる)ものと、参照されるものの対応がとれている必要があります。ご希望のコードでは、参照先を参照させることとなり、実体が不明になるのではないでしょうか。
つまり、きくちゃんさんの[2005-09-30 13:23]ですけど。
_____________________________________________________________________________
□ Posted by Jitta on 2005/10/02
じったのノート
□ Microsoft MVP :Visual Developer ASP/ASP.NET Oct.2005-Sept.2006
_________________
ジブ
大ベテラン
会議室デビュー日: 2005/09/22
投稿数: 135
投稿日時: 2005-10-02 17:21
JUNJUNさん、こんにちは、ジブです。

別件で忙しくてなかなかレスできなかったのですが
ようやく一息いれられそうな気配があるので、少し参加させていただければ幸いです。

シリアライズのことでは私もいろいろ悩んでいました。

標準的に準備されているシリアライザではいろいろな制限があって
結局私は自前のシリアライザ、デシリアライザを作り、今も機能拡張の途中です。

最大の課題はフレームワークが持つ多くのオブジェクトも自分自身でシリアライズしなければならないことでした。

そういうときにCodeDomの話題は非常に興味深いものでした。

いくつか調べてみましたが、あまりに技術資料が少なく、
そもそもどの程度の実用性があるのか若干疑いだしているところです。

自前のデシリアライザを作ってみて初めて解ったのですが
標準のデシリアライザの制限の妥当性について理解ができたように思えます。

たとえば引数なしのコンストラクタが必ず必要であるとか。
(そうでなくても可能ですが、クライアントクラスというよりコンテナクラスに
デシリアライズ時に引数付きでインスタンス作成するように要求できる仕組みを
準備しなくてはなりません。)

きわめて単純な構造でなければシリアライズできてもデシリアライズできないとか。

もしJUNJUNさんが実務に追われる立場でなく、純粋に勉強できる立場であるならば
自前でシリアライザを書いてみるのもおもしろいかもしれません。

えー。まず、それはそれとして。

ところで、私もCodeDomをよく知りたいという立場として逆質問させていだいてよろしいでしょうか?

まず、SampleClass1のシリアライザは作成されていますでしょうか?

CodeDomがSampleClass1を認識していないような気がするものですから。

私も今の作業が一段落したら、もう少し検証してみたいのですが
今は少し別件で時間がとれないので、お話を伺っている範囲で気づいた点をお聞きしたいと
申し訳ありません。



JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-10-03 20:23
Jitta様、説明ありがとうございます。
ジブ様、ご協力感謝いたします。

まず最初に、実際に間接的な代入を行っているサンプルを記述します。
コード:
'必要コード以外は省略
Private Sub InitializeComponent()
        Dim ListViewItem1 As System.Windows.Forms.ListViewItem = New System.Windows.Forms.ListViewItem("")
        Me.ListView1 = New System.Windows.Forms.ListView
        '
        'ListView1
        '
        Me.ListView1.Items.AddRange(New System.Windows.Forms.ListViewItem() {ListViewItem1})
End Sub


これはフォームにListViewを貼り付け、Itemを追加しただけです。
何気にツールを作っていた時に気づきました。
まさか、こんな身近にサンプルがあるとは・・・。盲点です。

次に、
引用:

ジブ様の書き込み (2005-10-02 17:21) より:
まず、SampleClass1のシリアライザは作成されていますでしょうか?
CodeDomがSampleClass1を認識していないような気がするものですから。


についてですが、シリアル化は(おそらく)成功しています。
InitializeComponentへの出力確認もできました。
以下にSerializeメソッドを記述します。
コード:
        Dim baseSerializer As CodeDomSerializer = CType(manager.GetSerializer(GetType(UserControl1).BaseType, GetType(CodeDomSerializer)), CodeDomSerializer)
        Dim codeObject As Object = baseSerializer.Serialize(manager, value)
        If TypeOf codeObject Is CodeStatementCollection Then

            Dim statements As CodeStatementCollection = CType(codeObject, CodeStatementCollection)

            Dim targetObject As CodeExpression = MyBase.SerializeToReferenceExpression(manager, value)

            If Not (targetObject Is Nothing) Then

                'コード生成:Dim temp As String = New String(New Char(0) {})    ← 本当は""を代入したかったがよくわからなかった。
                Dim variableDeclaration As New CodeVariableDeclarationStatement(GetType(String), "temp", New CodeObjectCreateExpression(GetType(String), New CodeArrayCreateExpression("System.Char", 1)))
                statements.Add(variableDeclaration)

                'コード生成:Me.UserControl11.Text = temp ← UserControl型なのでTextプロパティを持つ
                Dim fieldRef1 As New CodeFieldReferenceExpression(targetObject, "Text")
                Dim fieldRef2 As New CodeVariableReferenceExpression("temp")
                Dim as1 As New CodeAssignStatement(fieldRef1, fieldRef2)
                statements.Add(as1)

            End If

        End If

        Return codeObject



以上、これらを元に何かコメントをいただけたら幸いです。
宜しくお願いいたします。
ジブ
大ベテラン
会議室デビュー日: 2005/09/22
投稿数: 135
投稿日時: 2005-10-03 21:43
CodeDomとは直接関係ないかもしれませんが

NET Framework 開発者ガイドの「シリアル化のカスタマイズ」によると
引用:

オブジェクトは内側から外側に向かって再構築されるため、
逆シリアル化のときに呼び出しを行うメソッドは、望ましくない副作用を引き起こす可能性があります。
これは、呼び出しを行うメソッドが、
呼び出しの時点では逆シリアル化されていないオブジェクト参照を参照する可能性があるためです。
逆シリアル化対象のクラスが IDeserilizationCallback を実装している場合は、
オブジェクト グラフ全体が逆シリアル化された時点で OnDeserialization メソッドが自動的に呼び出されます。



おそらくデシリアライズの一般的な要件であるように思われます。

つまり、デシリアライザがどのような順序でおのおののクラスをデシリアライズするのかわかりませんが
なんらかの順序関係をデシリアライザに伝えてあげる必要があるのではないかなと思います。

ここに書かれている IDeserilizationCallbackについては全く資料がみあたりません。

CodeDomでもおそらく同様なことが起こっているのではないのかなと思います。

Dim temp as 。。。の位置をクラスの定義の前に置けるとかだといけるのかな?

それが可能なのか、または機能するのかは私にはわかりかねますが。
JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-10-04 13:26
ジブ様、貴重な情報ありがとうございます。

IDeserilizationCallbackを試しに実装してみました。
デザイン時では何も変化がありませんでした。
IDeserilizationCallbackはSystem.Runtime.Serialization名前空間です。
Runtimeに属するという事から実行時しか処理されないのかなと思い実行してみましたがやはり処理されません。
このインターフェースはいったい何なのでしょうか・・・。

「シリアル化のカスタマイズ」を見てみると他にも興味深そうな情報があるので、調べてます。
きくちゃん
ぬし
会議室デビュー日: 2003/08/01
投稿数: 854
お住まい・勤務地: 都内某所
投稿日時: 2005-10-05 09:13
JUNJUNさん、お早うございます。

引用:

これはフォームにListViewを貼り付け、Itemを追加しただけです。
何気にツールを作っていた時に気づきました。
まさか、こんな身近にサンプルがあるとは・・・。盲点です。


へぇ〜。ホントだ〜。

で、調べがついてるかも知れませんが、これって、コントロール側(DesignerSerializer も含む)の仕事じゃないみたいですね。

試しに ListViewItem をプロパティとして持つコントロール(正確に言うと IList を実装する ListViewItem のコレクション型で、DesignerSerializationVisibility 属性に Content が指定されたプロパティを持つコントロール)を作ってみたら、同様の初期化コードが出力されました。

そうなると、ListViewItem の 型コンバータの変換処理か、あるいは ListViewItem 自体のシリアル化・復元処理あたりで、何かやってるんでしょうねぇ…。

必要となる局面が訪れるかどうかは判りませんが、興味はあります。解明できたら、教えて下さい。

【追記】
ちなみにこれ↓は、
引用:

本当は""を代入したかったがよくわからなかった。


こんな感じ↓で。
コード:

Dim variableDeclaration As New CodeVariableDeclarationStatement(GetType(String), "temp", New CodePrimitiveExpression(""))




[ メッセージ編集済み 編集者: きくちゃん 編集日時 2005-10-05 09:20 ]
JUNJUN
常連さん
会議室デビュー日: 2004/11/29
投稿数: 24
投稿日時: 2005-10-05 20:53
こんばんわ。

引用:

きくちゃん様の書き込み(2005-10-05 09:13)より:
試しに ListViewItem をプロパティとして持つコントロール(正確に言うと IList を実装する ListViewItem のコレクション型で、DesignerSerializationVisibility 属性に Content が指定されたプロパティを持つコントロール)を作ってみたら、同様の初期化コードが出力されました。


これは私も気になったので直ぐに調べました。
私はコレクション型のクラスを作る自信がなかったのでListViewItemCollectionをそのまま使いました。
コード:
    Private _items As System.Windows.Forms.ListView.ListViewItemCollection

    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    Public Property Items() As System.Windows.Forms.ListView.ListViewItemCollection
        Get
            If _items Is Nothing Then
                _items = New System.Windows.Forms.ListView.ListViewItemCollection((Me.ListView1))
            End If
            Return _items
        End Get
        Set(ByVal Value As System.Windows.Forms.ListView.ListViewItemCollection)
            _items = Value
        End Set
    End Property


このコードはUserControl型を継承したクラスにListViewItemCollection型のプロパティを作成したものです。
このクラスをフォームに貼り付けるとヴィジュアルデザイナでListViewの様にItemsプロパティが表示されます。
このプロパティを設定するとInitializeComponentに目的のコードが生成されました。
この時、一つヒントを得ました。
それは、「ListViewItemCollectionのコンストラクタは引数にListView型を必要とする」点です。
おそらくコード生成時に親へ関連付ける為にあるのだろうと思います。

ListViewItemCollectionの要素となるListViewItemそのものは単純にInitializeComponentへ自らをシリアル化し、ListViewItemCollectionがそのコードと親(ListView)を関連付けるといった所なのでしょうか。

そう考えるとListViewItemにインスタンスのコード生成機能があるはずです。
まずはコード生成を確認しようと、先ほどのカスタムコントロールに以下のプロパティを追加しました。
コード:
    Private _testProp As New MyProperty
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _
    Public Property TestProp() As MyProperty
        Get
            Return _testProp
        End Get
        Set(ByVal Value As MyProperty)
            _testProp = Value
        End Set
    End Property

    Public Class MyProperty
        Inherits ListViewItem
    End Class


MyPropertyはListViewItemを継承しただけです。
このMyProperty型のTestPropを実装した後にフォームのヴィジュアルデザイナでプロパティを変更してみました。
ListViewItemの持つTagプロパティに"ABC"と入力した結果が以下の通りです。
コード:
    Private Sub InitializeComponent()
        Dim ListViewItem1 As System.Windows.Forms.ListViewItem = New System.Windows.Forms.ListViewItem("")
        Me.UserControl21 = New WindowsApplication1.UserControl2
        Me.SuspendLayout()
        '
        'UserControl21
        '
        Me.UserControl21.Location = New System.Drawing.Point(104, 44)
        Me.UserControl21.Name = "UserControl21"
        Me.UserControl21.TabIndex = 0
        ListViewItem1.Tag = "ABC"
        '
        'Form1
        '
        〜省略〜
    End Sub


InitializeComponent内部の変数としてListViewItem1が宣言されています。
また、Tagプロパティはこの変数を通して"ABC"と設定されています。

ここで、先ほどのMyPropertyクラスの型コンバータをただのExpandableObjectConverterにしてみました。
コード:
    <TypeConverter(GetType(ExpandableObjectConverter))> _
    Public Class MyProperty


先ほどと同じようにTagプロパティを"ABC"に変更し、InitializeComponentを見てみると以下のようになります。
コード:
        〜省略〜
        '
        'UserControl21
        '
        Me.UserControl21.Location = New System.Drawing.Point(152, 88)
        Me.UserControl21.Name = "UserControl21"
        Me.UserControl21.TabIndex = 0
        Me.UserControl21.TestProp.Tag = "abc"
        〜省略〜



ListViewItemのコンバータはExpandableObjectConverterを継承しているので、そこで自動生成のロジックが組まれているのでしょう。


だらだらとわかりきっている(かもしれない)事を並べ立てて申し訳ありません。。。
ただ、私としては以上のような結果から今後はListViewItemCollectionを調べていこうかなと思います。
多分コレクション系は他にも同じような事をしてそうですし。

ちなみに、私の考察に問題がある場合は是非ご指摘ください。
宜しくお願いいたします。

スキルアップ/キャリアアップ(JOB@IT)