.NETで作る!

.NETに関するあれこれ(C#、VB.NET)

CA1002、CA2227を避けつつシリアル化する

シリアル化処理をするクラスに配列のプロパティがあるのはよくあることですが、 何も考えずにコーディングすると以下の制約にひっかかることが多いと思います。

サンプルのコード

Public Property Hoges As New List(Of Hoge) 

CA1002: ジェネリック リストを公開しません

CA1002: ジェネリック リストを公開しません

Collection(Of T)などで公開して解決できます。なおIList(Of T)ではシリアル化ができないのでNG。 なお、Collection(Of T)名前空間System.Collections.ObjectModelですので、適宜インポートしてください。

Public Property Hoges As New Collection(Of Hoge) 

CA2227: Collection プロパティは読み取り専用でなければなりません

CA2227: Collection プロパティは読み取り専用でなければなりません

ReadOnlyプロパティにすることで解決します。

Pricvate _hoges As New Collection(Of Hoge)
Public Property Hoges As Collection(Of Hoge) 
    Get
        Return _hoges
    End Get
End Property

が、「CA2227」の対応したことでシリアル化の対象からはずれてしまいました…

読み取り専用コレクションをシリアル化する

XML シリアル化の概要

こちらを見ると

XML シリアル化では、メソッド、インデクサー、プライベート フィールド、または読み取り専用プロパティ (読み取り専用コレクションを除く) は変換されません。

とあります。読み取り専用プロパティはXMLシリアル化されないとありますが、但し書きで「読み取り専用コレクションは除く」とされています。ん?さっき読み取り専用コレクションにしたのにシリアル化されなかったんですが…

もう少し読んでいきましょう。

シリアル化できる項目

XmLSerializer クラスを使用して、次の項目をシリアル化できます。

  • パブリックな読み取り/書き込みプロパティとパブリック クラスのフィールド
  • ICollection または IEnumerable を実装するクラス

    注 パブリック プロパティではなく、コレクションのみがシリアル化されます。

いまだよくわかりません。Collection(Of T)ってICollection を実装していないの?そんな訳ないよね。*1

もう少し読んでいきましょう。

XML シリアル化に関する考慮事項

IEnumerable または ICollection を異なる方法で実装しているクラスは、次のような特定の要件を満たしていれば、XmlSerializer で処理できます。

  • IEnumerable を実装するクラスは、単一のパラメーターを受け取るパブリックな Add メソッドを実装している必要があります。 Add メソッドのパラメーターは、GetEnumerator メソッドによって返される IEnumerator.Current プロパティから返される型と一致している (ポリモーフィックである) 必要があります。

  • IEnumerable の他に ICollection も実装するクラス (CollectionBase など) は、整数を受け取るパブリックなインデックス付き Item プロパティ (C# の場合はインデクサー) と、integer 型のパブリックな Count プロパティを持つ必要があります。 Add メソッドに渡されるパラメーターは、Item プロパティから返された型と同じ型か、またはその型の基本型の 1 つである必要があります。

  • ICollection を実装するクラスの場合、シリアル化される値は、GetEnumerator を呼び出して取得されるのではなく、インデックス付き Item プロパティから取得されます。 また、パブリック フィールドとパブリック プロパティは、別のコレクション クラス (ICollection を実装するクラス) を返すパブリック フィールドを除き、シリアル化されません。 例については、「XML シリアル化の例」を参照してください。

正直なところ、よくわかりません。独自でICollectionを実装すればうまくいきそうなので、そのようにします。

Pricvate _hoges As New HogeCollection
Public Property Hoges As HogeCollection
    Get
        Return _hoges
    End Get
End Property

Public Class HogeCollection
    Implements ICollection(Of Hoge)

    Private Property Items As New Collection(Of Hoge)

    Public Sub Add(item As Hoge) Implements ICollection(Of Hoge).Add
        Me.Items.Add(item)
    End Sub

    Public Sub Clear() Implements ICollection(Of Hoge).Clear
        Me.Items.Clear()
    End Sub

    Public Function Contains(item As Hoge) As Boolean Implements ICollection(Of Hoge).Contains
        Return Me.Items.Contains(item)
    End Function

    Public Sub CopyTo(array() As Hoge, arrayIndex As Integer) Implements ICollection(Of Hoge).CopyTo
        Me.CopyTo(array, arrayIndex)
    End Sub

    Public ReadOnly Property Count As Integer Implements ICollection(Of Hoge).Count
        Get
            Return Me.Items.Count
        End Get
    End Property

    Public ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of Hoge).IsReadOnly
        Get
            Return False
        End Get
    End Property

    Public Function Remove(item As Hoge) As Boolean Implements ICollection(Of Hoge).Remove
        Return Me.Items.Remove(item)
    End Function

    Public Function GetEnumerator() As IEnumerator(Of Hoge) Implements IEnumerable(Of Hoge).GetEnumerator
        Return Me.Items.GetEnumerator
    End Function

    Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
        Return Me.Items.GetEnumerator
    End Function
End Class

このコードでシリアル化されることは確認できました。若干消化不良ですが良しとしましょう。

. .