データ コントラクトのコレクション型

"コレクション" は、特定の型の項目のリストです。.NET Framework では、このようなリストは、配列や他のさまざまな型を使用して表すことができます (ジェネリック List、ジェネリック BindingListStringCollection、または ArrayList)。たとえば、コレクションでは指定された顧客のアドレスのリストを保持できます。これらのコレクションは、実際の型に関係なく、リスト コレクションと呼びます。

コレクションには、ある項目 ("キー") と別の項目 ("値") の関連付けを表す特殊な形式のものがあります。これらのコレクションは、.NET Framework では Hashtable やジェネリック ディクショナリなどの型によって表されます。たとえば、関連付けコレクションでは、都市 ("キー") をその人口 ("値") に関連付けることができます。これらのコレクションは、実際の型に関係なく、ディクショナリ コレクションと呼びます。

データ コントラクト モデルでは、コレクションは特別な扱いを受けます。

配列やジェネリック コレクションを含め、IEnumerable インターフェイスを実装する型はコレクションとして認識されます。これらの中で、IDictionary インターフェイスまたはジェネリック IDictionary インターフェイスを実装する型がディクショナリ コレクションであり、他はすべてリスト コレクションです。

Add というメソッドと既定のコンストラクターを持つなど、コレクション型のその他の要件については、以降のセクションで詳しく説明します。これにより、コレクション型を確実にシリアル化および逆シリアル化できます。これは、直接サポートされないコレクションもあることを意味します。たとえば、ジェネリック ReadOnlyCollection (既定のコンストラクターを持たないため) や、Stack (エントリを追加するためのメソッドが、Add ではなく Push であるため) などは直接サポートされません。これらの制限を回避する方法については、このトピックで後述する「コレクション インターフェイス型と読み取り専用コレクションの使用」を参照してください。

コレクションに含まれる型は、データ コントラクト型である必要があります。それ以外の場合は、シリアル化可能な型であることが必要です。詳細については、次のトピックを参照してください。 データ コントラクト シリアライザーでサポートされる型.

有効なコレクションと見なされるものと見なされないもの、およびコレクションをシリアル化する方法詳細情報、このトピックの「コレクションの高度な規則」でコレクションのシリアル化に関する情報を参照してください。

交換可能なコレクション

同じ型のすべてのリスト コレクションは、同じデータ コントラクトを持つと見なされます (このトピックで後述するように、CollectionDataContractAttribute 属性を使用してカスタマイズされている場合を除きます)。たとえば、次のデータ コントラクトは等価です。

[DataContract(Name = "PurchaseOrder")]
public class PurchaseOrder1
{
    [DataMember]
    public string customerName;
    [DataMember]
    public Collection<Item> items;
    [DataMember]
    public string[] comments;
}

[DataContract(Name = "PurchaseOrder")]
public class PurchaseOrder2
{
    [DataMember]
    public string customerName;
    [DataMember]
    public List<Item> items;
    [DataMember]
    public BindingList<string> comments;
}

この 2 つのデータ コントラクトでは、いずれも次のコードに似た XML が生成されます。

<PurchaseOrder>
    <customerName>...</customerName>
    <items>
        <Item>...</Item>
        <Item>...</Item>
        <Item>...</Item>
        ...
    </items>
    <comments>
        <string>...</string>
        <string>...</string>
        <string>...</string>
        ...
    </comments>
</PurchaseOrder>

コレクションの可換性により、たとえば、サーバーではパフォーマンスを確保するように最適化されたコレクション型を使用し、クライアントではユーザー インターフェイス コンポーネントにバインドするよう設計されたコレクション型を使用することが可能です。

リスト コレクションと同様に、(CollectionDataContractAttribute 属性によってカスタマイズされている場合を除き) キーおよび値の型が同じであるすべてのディクショナリ コレクションは、同じデータ コントラクトを持つと見なされます。

コレクションの等価性に関する限り、重要となるのはデータ コントラクト型のみであり、.NET 型は影響しません。つまり、Type1 と Type2 のデータ コントラクトが等価であれば、Type1 のコレクションは Type2 のコレクションと等価と見なされます。

非ジェネリック コレクションは、Object 型のジェネリック コレクションと同じデータ コントラクトを持つと見なされます (たとえば、ArrayListObject のジェネリック List のデータ コントラクトは同じです)。

コレクション インターフェイス型と読み取り専用コレクションの使用

コレクション インターフェイス型 (IEnumerableIDictionary、ジェネリック IDictionary、またはこれらのインターフェイスから派生したインターフェイス) も、実際のコレクション型のコレクション データ コントラクトと等価のコレクション データ コントラクトを持つと見なされます。したがって、シリアル化する型をコレクション インターフェイス型として宣言できます。この結果は、実際のコレクション型を使用した場合と同じになります。たとえば、次のデータ コントラクトは等価です。

[DataContract(Name="Customer")] public class Customer1
{
    [DataMember] public string customerName;
    [DataMember] public Collection<Address> addresses;
}

[DataContract(Name="Customer")] public class Customer2
{
    [DataMember] public string customerName;
    [DataMember] public ICollection<Address> addresses;
}

シリアル化の実行時には、宣言された型がインターフェイスの場合、使用される実際のインスタンスの型はそのインターフェイスを実装する任意の型になります。前述の制限 (既定のコンストラクターと Add メソッドを持つ) は適用されません。たとえば、ジェネリック ReadOnlyCollection 型のデータ メンバーを直接宣言できない場合でも、Customer2 のアドレスを Address のジェネリック ReadOnlyCollection のインスタンスに設定できます。

逆シリアル化の実行時には、宣言された型がインターフェイスの場合、宣言されたインターフェイスを実装する型がシリアル化エンジンによって選択され、インスタンス化されます。この場合、既知の型機構 (「既知のデータ コントラクト型」を参照) は無効になります。型の選択は WCF に組み込まれています。

コレクション型のカスタマイズ

コレクション型は、複数の用途を持つ CollectionDataContractAttribute 属性を使用することによってカスタマイズできます。

コレクション型をカスタマイズすると、コレクションの可換性が損なわれるため、一般に、できるだけこの属性を適用しないようにすることをお勧めします。この問題詳細情報、このトピックで後述する「コレクションの高度な規則」を参照してください。

コレクション データ コントラクトの名前付け

コレクション型の名前付け規則は、「データ コントラクト名」で説明する通常のデータ コントラクト型の名前付けの規則に似ていますが、重要な違いがいくつかあります。

  • 名前をカスタマイズする際に、DataContractAttribute 属性ではなく、CollectionDataContractAttribute 属性を使用します。CollectionDataContractAttribute 属性にも、Name プロパティと Namespace プロパティがあります。

  • CollectionDataContractAttribute 属性を適用しない場合、コレクション型の既定の名前と名前空間は、コレクションに含まれる型の名前と名前空間によって決まります。これらは、コレクション型自体の名前と名前空間の影響を受けません。型の一例を次に示します。

    public CustomerList1 : Collection<string> {}
    public StringList1 : Collection<string> {}
    

これらの型のデータ コントラクト名は、"CustomerList1" や "StringList1" ではなく、どちらも "ArrayOfstring" になります。つまり、ルート レベルでこれらの型のどちらをシリアル化しても、次のコードのような XML が生成されます。

<ArrayOfstring>
    <string>...</string>
    <string>...</string>
    <string>...</string>
    ...
</ArrayOfstring>

この名前付け規則が選択されたのは、文字列のリストを表すカスタマイズされていないすべての型が同じデータ コントラクトと XML 表現を持つことができるようにするためです。これにより、コレクションの可換性が実現されています。この例では、CustomerList1 と StringList1 は完全に交換可能です。

ただし、CollectionDataContractAttribute 属性を適用すると、この属性でプロパティが設定されていない場合でも、コレクションはカスタマイズされたコレクション データ コントラクトになります。この場合、コレクション データ コントラクトの名前と名前空間は、コレクション型自体によって決まります。型の一例を次に示します。

[CollectionDataContract] 
public class CustomerList2 : Collection<string> {}

シリアル化すると、生成される XML は次のようになります。

<CustomerList2>
    <string>...</string>
    <string>...</string>
    <string>...</string>
    ...
</CustomerList2>

これは、カスタマイズされていない型の XML 表現と等価ではなくなっていることに注意してください。

  • Name プロパティと Namespace プロパティを使用して、名前付けをさらにカスタマイズできます。次のクラスを参照してください。

     [CollectionDataContract(Name="cust_list")]
    public class CustomerList3 : Collection<string> {}
    

生成される XML は次のようになります。

<cust_list>
    <string>...</string>
    <string>...</string>
    <string>...</string>
    ...
</cust_list>

詳細については、次のトピックを参照してください。このトピックで後述される「コレクションの高度な規則」を参照してください。

リスト コレクション内の反復される要素名のカスタマイズ

リスト コレクションには、反復されるエントリが含まれています。通常、反復される各エントリは、コレクションに含まれる型のデータ コントラクト名に従って名前が付けられた要素として表されます。

CustomerList の各例では、コレクションに文字列が含まれていました。文字列プリミティブ型のデータ コントラクト名は "string" であるため、反復される要素は "<string>" でした。

ただし、CollectionDataContractAttribute 属性の ItemName プロパティを使用すると、この反復される要素名をカスタマイズできます。型の一例を次に示します。

[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string>  {}

生成される XML は次のようになります。

<CustomerList4>
    <customer>...</ customer>
    <customer>...</customer>
    <customer>...</customer>
    ...
</CustomerList4>

反復される要素の名前空間は、コレクション データ コントラクトの名前空間と常に同じです。前述のように、コレクション データ コントラクトの名前空間は、Namespace プロパティを使用してカスタマイズできます。

ディクショナリ コレクションのカスタマイズ

ディクショナリ コレクションは、基本的にエントリのリストです。各エントリにはキーが含まれ、その後に値が続きます。通常のリストと同様に、反復される要素に対応する要素名は、ItemName プロパティを使用して変更できます。

また、キーと値を表す要素名は、KeyName プロパティと ValueName プロパティを使用して変更できます。これらの要素の名前空間は、コレクション データ コントラクトの名前空間と同じです。

型の一例を次に示します。

[CollectionDataContract
    (Name = "CountriesOrRegionsWithCapitals", 
    ItemName = "entry", 
    KeyName = "countryorregion", 
    ValueName = "capital")]
public class CountriesOrRegionsWithCapitals2 : Dictionary<string, string> { }

シリアル化すると、生成される XML は次のようになります。

<CountriesOrRegionsWithCapitals>
    <entry>
        <countryorregion>USA</countryorregion>
        <capital>Washington</capital>
    </entry>
    <entry>
        <countryorregion>France</countryorregion>
        <capital>Paris</capital>
    </entry>
    ...
</CountriesOrRegionsWithCapitals>

ディクショナリ コレクション詳細情報、このトピックで後述される「コレクションの高度な規則」を参照してください。

コレクションと既知の型

コレクション型を、他のコレクションまたはコレクション インターフェイスの代わりにポリモーフィックに使用する場合は、既知の型に追加する必要はありません。たとえば、IEnumerable 型のデータ メンバーを宣言し、このデータ メンバーを使用して ArrayList のインスタンスを送信する場合、ArrayList を既知の型に追加する必要はありません。

コレクションを、コレクション型以外の型の代わりにポリモーフィックに使用する場合は、コレクションを既知の型に追加する必要があります。たとえば、Object 型のデータ メンバーを宣言し、このデータ メンバーを使用して ArrayList のインスタンスを送信する場合は、ArrayList を既知の型に追加する必要があります。

この場合、等価のコレクションをポリモーフィックにシリアル化することはできません。たとえば、前述の例で、ArrayList を既知の型のリストに追加すると、Array of Object クラスが等価のデータ コントラクトを持っていても、これを割り当てることはできなくなります。これは、コレクション型以外の型のシリアル化における、通常の既知の型の動作と変わりません。しかし、コレクションが等価であることはごく一般的なことであるため、コレクションに関しては、この動作を理解しておくことが特に重要となります。

シリアル化では、指定されたデータ コントラクトの特定のスコープで許可される既知の型は 1 つだけであり、等価のコレクションはすべて同じデータ コントラクトを持ちます。つまり、前述の例では、同じスコープで ArrayListArray of Object の両方を既知の型に追加することはできません。先ほども説明したように、これはコレクション型以外の型での既知の型の動作と同じですが、コレクションではこの動作を理解しておくことが特に重要です。

コレクションの内容に既知の型が必要になることもあります。たとえば、ArrayListType1Type2 のインスタンスを実際に含める場合、この両方の型を既知の型に追加する必要があります。

コレクションと既知の型を使用して、適切に構築されたオブジェクト グラフの例を次に示します (この例には、多少不自然な部分があります。実際のアプリケーションでは、通常、次のデータ メンバーを Object として定義することはないため、既知の型やポリモーフィズムの問題が発生することはありません)。

[DataContract]
public class Employee
{
    [DataMember] public string name = "John Doe";
    [DataMember] public Payroll payrollRecord;
    [DataMember] public Training trainingRecord;
}

[DataContract]
[KnownType(typeof(int[]))] //required because int[] is used polymorphically
[KnownType(typeof(ArrayList))] //required because ArrayList is used polymorphically
public class Payroll
{
    [DataMember] public object salaryPayments = new int[12];
    //float[] not needed in known types because polymorphic assignment is to another collection type
    [DataMember] public IEnumerable<float> stockAwards = new float[12];
    [DataMember] public object otherPayments = new ArrayList();
}

[DataContract]
[KnownType(typeof(List<object>))] 
//required because List<object> is used polymorphically
//does not conflict with ArrayList above because it's a different scope, 
//even though it's the same data contract
[KnownType(typeof(InHouseTraining))] //Required if InHouseTraining can be used in the collection
[KnownType(typeof(OutsideTraining))] //Required if OutsideTraining can be used in the collection
public class Training
{
    [DataMember] public object training = new List<object>();
}

[DataContract] public class InHouseTraining {
    //code omitted
}

[DataContract] public class OutsideTraining {
    //code omitted
}

逆シリアル化では、宣言された型がコレクション型の場合、実際に送信された型に関係なく、宣言された型がインスタンス化されます。宣言された型がコレクション インターフェイスの場合は、既知の型に関係なく、インスタンス化される型がデシリアライザーによって選択されます。

また、逆シリアル化では、宣言された型がコレクション型ではないときにコレクション型を送信する場合、既知の型リストから一致するコレクション型が選択されます。逆シリアル化では、コレクション インターフェイス型を既知の型のリストに追加できます。この場合、逆シリアル化エンジンによって、インスタンス化する型が再度選択されます。

コレクションと NetDataContractSerializer クラス

NetDataContractSerializer クラスの使用時には、配列ではないカスタマイズされていないコレクション型 (CollectionDataContractAttribute 属性を使用していないコレクション型) は、その特殊な意味を失います。

SerializableAttribute 属性でマークされたカスタマイズされていないコレクション型でも、SerializableAttribute 属性または ISerializable インターフェイスの規則に従って、NetDataContractSerializer クラスによってシリアル化できます。

NetDataContractSerializer クラスを使用している場合でも、カスタマイズされたコレクション型、コレクション インターフェイス、および配列は、コレクションとして扱われます。

コレクションとスキーマ

すべての等価のコレクションは、XML スキーマ定義言語 (XSD: XML Schema Definition Language) スキーマで同様に表現されます。このため、生成されたクライアント コードでもサーバーと同じコレクション型になることは通常ありません。たとえば、サーバーで Integer データ メンバーのジェネリック List を含むデータ コントラクトを使用していても、生成されたクライアント コードでは、この同じデータ メンバーが整数の配列になることがあります。

ディクショナリ コレクションは、それがディクショナリであることを示す WCF 固有のスキーマ注釈でマークされます。そうしないと、キーと値を持つエントリが含まれた単純なリストと区別できなくなります。データ コントラクト スキーマでのコレクションの表現方法の正確な記述については、「データ コントラクト スキーマの参照」を参照してください。

既定では、コードをインポートする際に、カスタマイズされていないコレクションの型は生成されません。リスト コレクション型のデータ メンバーは配列としてインポートされ、ディクショナリ コレクション型のデータ メンバーはジェネリック ディクショナリとしてインポートされます。

ただし、カスタマイズされたコレクションの場合、CollectionDataContractAttribute 属性でマークされた個別の型が生成されます (スキーマにおけるカスタマイズされたコレクション型とは、既定の名前空間、名前、反復される要素名、またはキー/値要素名を使用しない型のことです)。これらの型は、リスト型のジェネリック List およびディクショナリ型のジェネリック ディクショナリから派生した空の型です。

たとえば、サーバーで次のような型を使用するとします。

[DataContract]
public class CountryOrRegion
{
    [DataMember]
    public Collection<string> officialLanguages;
    [DataMember]
    public List<DateTime> holidays;
    [DataMember]
    public CityList cities;
    [DataMember]
    public ArrayList otherInfo;
}

public class Person
{
    public Person(string fName, string lName)
    {
        this.firstName = fName;
        this.lastName = lName;
    }

    public string firstName;
    public string lastName;
}

public class PeopleEnum : IEnumerator
{
    public Person[] _people;

    // Enumerators are positioned before the first element
    // until the first MoveNext() call.
    int position = -1;

    public PeopleEnum(Person[] list)
    {
        _people = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _people.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    public object Current
    {
        get
        {
            try
            {
                return _people[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

[CollectionDataContract(Name = "Cities", ItemName = "city", KeyName = "cityName", ValueName = "population")]
public class CityList : IDictionary<string, int>, IEnumerable<System.Collections.Generic.KeyValuePair<string, int>>
{

    private Person[] _people = null;

    public bool ContainsKey(string s) { return true; }
    public bool Contains(string s) { return true; }
    public bool Contains(KeyValuePair<string, int> item) { return (true); }
    public void Add(string key, int value) { }
    public void Add(KeyValuePair<string, int> keykValue) { }
    public bool Remove(string s) { return true; }
    public bool TryGetValue(string d, out int i)
    {
        i = 0; return (true);
    }

    /*
    [TypeConverterAttribute(typeof(SynchronizationHandlesTypeConverter))]
    public ICollection<string> SynchronizationHandles {
        get { return (System.Collections.Generic.ICollection<string>) new Stack<string> (); }
        set { }
    }*/

    public ICollection<string> Keys
    {
        get
        {
            return (System.Collections.Generic.ICollection<string>)new Stack<string>();
        }
    }

    public int this[string s]
    {
        get
        {
            return 0;
        }
        set
        {
        }
    }

    public ICollection<int> Values
    {
        get
        {
            return (System.Collections.Generic.ICollection<int>)new Stack<string>();
        }
    }

    public void Clear() { }
    public void CopyTo(KeyValuePair<string, int>[] array, int index) { }
    public bool Remove(KeyValuePair<string, int> item) { return true; }
    public int Count { get { return 0; } }
    public bool IsReadOnly { get { return true; } }

    IEnumerator<KeyValuePair<string, int>>
        System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, int>>.GetEnumerator()
    {
        return (IEnumerator<KeyValuePair<string, int>>)new PeopleEnum(_people); ;
    }

    public IEnumerator GetEnumerator()
    {

        return new PeopleEnum(_people);

    }

}

スキーマをエクスポートし、再度インポートすると、生成されるクライアント コードは次のようになります (わかりやすくするために、プロパティの代わりにフィールドを示しています)。

生成されるコードで、既定の型と異なる型を使用することが必要になる場合があります。たとえば、データ メンバーをユーザー インターフェイス コンポーネントにバインドしやすくするために、データ メンバーの通常の配列ではなく、ジェネリック BindingList を使用することがあります。

生成するコレクション型を選択するには、スキーマをインポートするときに、使用するコレクション型のリストを ImportOptions オブジェクトの ReferencedCollectionTypes プロパティに渡します。これらの型は、"参照されるコレクション型" と呼ばれます。

ジェネリック型を参照する場合は、完全なオープン ジェネリックまたは完全なクローズ ジェネリックであることが必要です。

Aa347850.note(ja-jp,VS.100).gif注 :
Svcutil.exe ツールを使用すると、/collectionType コマンド ライン スイッチ (短縮形 : /ct) を使用して、この参照を実現できます。/reference スイッチ (短縮形 : /r) を使用して、参照されるコレクション型のアセンブリも指定する必要があることに注意してください。型がジェネリックの場合は、型の後に逆引用符とジェネリック パラメーターの数を指定する必要があります。逆引用符 (`) は、単一引用符 (‘) と混同しないでください。複数の参照されるコレクション型を指定するには、/collectionType スイッチを複数回使用します。

たとえば、すべてのリストをジェネリック List としてインポートするには、次のように指定します。

svcutil.exe MyService.wsdl MyServiceSchema.xsd /r:C:\full_path_to_system_dll\System.dll /ct:System.Collections.Generic.List`1

コレクションをインポートするときに、参照されるコレクション型のこのリストがスキャンされ、最も一致するコレクションが見つかると、そのコレクションがデータ メンバー型 (カスタマイズされていないコレクションの場合)、または派生元となる基本型 (カスタマイズされたコレクションの場合) として使用されます。ディクショナリはディクショナリと照合され、リストはリストと照合されます。

たとえば、参照される型のリストにジェネリック BindingListHashtable を追加した場合、前述の例で生成されるクライアント コードは次のようになります。

[DataContract]
public class CountryOrRegion3
{
    [DataMember]
    public BindingList<string> officialLanguages;
    [DataMember]
    public BindingList<DateTime> holidays;
    [DataMember]
    public Cities cities;
    [DataMember]
    public BindingList<object> otherInfo;
}

[CollectionDataContract(ItemName = "city", KeyName = "cityName", ValueName = "population")]
public class Cities3 : Hashtable { }

参照されるコレクション型の一部として、コレクション インターフェイス型を指定できますが、無効なコレクション型 (Add メソッドまたはパブリック コンストラクターを持たないコレクション型など) を指定することはできません。

クローズ ジェネリック型は、合致度が最も高いと見なされます (非ジェネリック型は、Object のクローズ ジェネリックと同等に扱われます)。たとえば、DateTime のジェネリック List、ジェネリック BindingList (オープン ジェネリック)、および ArrayList が参照されるコレクション型である場合、次のようなコードが生成されます。

[DataContract]
public class CountryOrRegion4
{
    [DataMember]
    public string[] officialLanguages;
    [DataMember]
    public DateTime[] holidays;
    [DataMember]
    public Cities cities;
    [DataMember]
    public object[] otherInfo;
}

[CollectionDataContract(ItemName = "city", KeyName = "cityName", ValueName = "population")]
public class Cities4 : Dictionary<string, int> { }

リスト コレクションの場合、以下の表内のケースだけがサポートされます。

参照される型 参照される型で実装されるインターフェイス 型の処理

非ジェネリックまたはクローズ ジェネリック (任意の数のパラメーター)

非ジェネリック

MyType : IList

または

MyType<T> : IList

ここでは、T=int

Object のクローズ ジェネリック (例 : IList<object>)

非ジェネリックまたはクローズ ジェネリック (コレクション型と必ずしも一致するわけではない任意の数のパラメーター)

クローズ ジェネリック

MyType : IList<string>

または

MyType<T> : IList<string> ここでは、T=int

クローズ ジェネリック (例 : IList<string>)

任意の数のパラメーターを持つクローズ ジェネリック

型のパラメーターのいずれかを使用するオープン ジェネリック

MyType<T,U,V> : IList<U>

ここでは、T=int、U=string、V=bool

クローズ ジェネリック (例 : IList<string>)

パラメーターを 1 つ持つオープン ジェネリック

型のパラメーターを使用するオープン ジェネリック

MyType<T> : IList<T>、T はオープン

オープン ジェネリック (例 : IList<T>)

型が複数のリスト コレクション インターフェイスを実装している場合、以下の制限が適用されます。

  • 型が、異なる型のジェネリック IEnumerable (またはその派生インターフェイス) を複数回実装している場合、その型は有効な参照されるコレクション型とは見なされず、無視されます。これは、一部の実装が無効であったり、オープン ジェネリックを使用していたりする場合も該当します。たとえば、型が int のジェネリック IEnumerable と、T のジェネリック IEnumerable を実装している場合、int の参照されるコレクションとしても、その他の型の参照されるコレクションとしても使用されることはありません。この場合、型が int を受け入れる Add メソッドと T 型のパラメーターを受け入れる Add メソッドのいずれか、または両方を持っているかどうかは関係ありません。

  • 型がジェネリック コレクション インターフェイスと IList を実装している場合、ジェネリック コレクション インターフェイスが Object 型のクローズ ジェネリックでない限り、参照されるコレクション型として使用されることはありません。

ディクショナリ コレクションの場合、以下の表内のケースだけがサポートされます。

参照される型 参照される型で実装されるインターフェイス 型の処理

非ジェネリックまたはクローズ ジェネリック (任意の数のパラメーター)

IDictionary

MyType : IDictionary

または

MyType<T> : IDictionary ここでは、T=int

クローズ ジェネリック IDictionary<object,object>

クローズ ジェネリック (任意の数のパラメーター)

IDictionary、クローズ

MyType<T> : IDictionary<string, bool> ここでは、T=int

クローズ ジェネリック (例 : IDIctionary<string,bool>)

クローズ ジェネリック (任意の数のパラメーター)

ジェネリック IDictionary、キーまたは値の一方がクローズ。もう一方はオープンで、型のパラメーターのいずれかを使用

MyType<T,U,V> : IDictionary<string,V> int float ここでは、T=bool、U=、V=

または

MyType<Z> : IDictionary<Z,bool> ここでは、Z=string

クローズ ジェネリック (例 : IDictionary<string,bool>)

クローズ ジェネリック (任意の数のパラメーター)

ジェネリック IDictionary、キーと値の両方がオープンであり、それぞれ型のパラメーターのいずれかを使用

MyType<T,U,V> : IDictionary<V,U> int bool ここでは、T=string、U=、V=

クローズ ジェネリック (例 : IDictionary<string,bool>)

オープン ジェネリック (2 つのパラメーター)

ジェネリック IDictionary、オープン、型のジェネリック パラメーターの両方をその出現順に使用

MyType<K,V> : IDictionary<K,V>、K と V は共にオープン

オープン ジェネリック (例 : IDictionary<K,V>)

型が IDictionary とジェネリック IDictionary の両方を実装している場合、ジェネリック IDictionary だけが考慮されます。

部分的なジェネリック型の参照はサポートされていません。

重複は許可されていません。たとえば、Integer のジェネリック List と、Integer のジェネリック コレクションの両方を ReferencedCollectionTypes に追加することはできません。これは、スキーマで整数のリストが見つかったときに、どちらを使用するかを判断できなくなるためです。もっとも、このような重複が検出されるのは、それが問題になるような型がスキーマ中にある場合だけです。たとえば、インポートしているスキーマ中に整数のリストがない場合に、Integer のジェネリック ListInteger のジェネリック コレクションの両方が ReferencedCollectionTypes にあっても問題はありませんが、いずれも使用されません。

コレクションの高度な規則

コレクションのシリアル化

コレクションのシリアル化規則を以下に示します。

  • コレクション型は、組み合わせる (コレクションのコレクションを持つ) ことができます。ジャグ配列は、コレクションのコレクションとして扱われます。多次元配列はサポートされていません。

  • バイト配列と XmlNode の配列は、コレクションではなく、プリミティブとして扱われる特殊な配列の型です。バイト配列をシリアル化すると、バイトごとの個別の要素ではなく、Base64 でエンコードされたデータのチャンクを含む単一の XML 要素が生成されます。XmlNode の配列の処理方法詳細情報、「データ コントラクトの XML および ADO.NET の種類」を参照してください。これらの特殊な型もコレクションに参加できます。バイト配列の配列の場合、複数の XML 要素が生成され、各要素に Base64 でエンコードされたデータのチャンクが含まれます。

  • DataContractAttribute 属性をコレクション型に適用した場合、型はコレクションとしてではなく、通常のデータ コントラクト型として扱われるようになります。

  • コレクション型が IXmlSerializable インターフェイスを実装している場合、次の規則が適用されます。myType:IList<string>, IXmlSerializable 型と仮定します。

    • 宣言型が IList<string> の場合、この型はリストとしてシリアル化されます。

    • 宣言型が myType の場合、この型は IXmlSerializable としてシリアル化されます。

    • 宣言型が IXmlSerializable の場合は、既知の型のリストに myType を追加しているときにのみ、この型が IXmlSerializable としてシリアル化されます。

  • コレクションは、以下の表に示すメソッドを使用してシリアル化および逆シリアル化されます。

コレクション型の実装 シリアル化で呼び出されるメソッド 逆シリアル化で呼び出されるメソッド

ジェネリック IDictionary

get_Keys,get_Values

ジェネリック Add

IDictionary

get_Keys,get_Values

Add

ジェネリック IList

ジェネリック IList インデクサー

ジェネリック Add

ジェネリック ICollection

列挙子

ジェネリック Add

IList

IList インデクサー

Add

ジェネリック IEnumerable

GetEnumerator

適切な型 (ジェネリック パラメーターの型、またはその基本型のいずれか) のパラメーターを 1 つ受け取る Add という非静的メソッド。このようなメソッドは、シリアル化と逆シリアル化の両方で、シリアライザーがコレクション型をコレクションとして処理するために必要になります。

IEnumerable (およびこのインターフェイスから派生した ICollection)

GetEnumerator

Object 型のパラメーターを 1 つ受け取る Add という非静的メソッド。このようなメソッドは、シリアル化と逆シリアル化の両方で、シリアライザーがコレクション型をコレクションとして処理するために必要になります。

前の表に示すコレクション インターフェイスは、優先度の高いものから順に並べられています。たとえば、型が IList とジェネリック IEnumerable の両方を実装している場合、IList の規則に従って、コレクションがシリアル化および逆シリアル化されます。

  • 逆シリアル化では、すべてのコレクションが、まず、既定のコンストラクターを呼び出して型のインスタンスを作成することによって逆シリアル化されます。既定のコンストラクターは、シリアル化と逆シリアル化の両方で、シリアライザーがコレクション型をコレクションとして処理するために必要となります。

  • 同じジェネリック コレクション インターフェイスが複数回実装されており (型が Integer のジェネリック ICollection と、String のジェネリック ICollection の両方を実装している場合など)、優先度がより高いインターフェイスが見つからない場合は、コレクションが有効なコレクションとして扱われません。

  • コレクション型には SerializableAttribute 属性を適用でき、ISerializable インターフェイスを実装できます。これらはいずれも無視されます。ただし、型がコレクション型の要件を完全に満たしていない場合 (Add メソッドがないなど)、その型はコレクション型と見なされないため、SerializableAttribute 属性と ISerializable インターフェイスを使用して、型をシリアル化できるかどうかが判断されます。

  • コレクションをカスタマイズするために、コレクションに CollectionDataContractAttribute 属性を適用した場合、上記の SerializableAttribute 代替機構は除去されます。カスタマイズされたコレクションがコレクション型の要件を満たしていない場合には、代わりに InvalidDataContractException 例外がスローされます。多くの場合、この例外文字列には、指定された型が有効なコレクションと見なされない理由を説明する情報 (Add メソッドがない、既定のコンストラクターがないなど) が含まれるため、CollectionDataContractAttribute 属性を適用することは、しばしばデバッグに役立ちます。

コレクションの名前付け

コレクションの名前付け規則を以下に示します。

  • Namespace を使用してオーバーライドしていない場合、すべてのディクショナリ コレクション データ コントラクト、およびプリミティブ型を含むリスト コレクション データ コントラクトの既定の名前空間は、https://schemas.microsoft.com/2003/10/Serialization/Arrays です。このために、組み込みの XSD 型に割り当てられた型と、charTimespan、および Guid の各型がプリミティブと見なされます。

  • Namespace を使用してオーバーライドしていない場合、プリミティブ型以外の型を含むコレクション型の既定の名前空間は、コレクションに含まれる型のデータ コントラクト名前空間と同じです。

  • Name を使用してオーバーライドしていない場合、リスト コレクション データ コントラクトの既定の名前は、文字列 "ArrayOf" とコレクションに含まれる型のデータ コントラクト名を組み合わせたものです。たとえば、Integers のジェネリック List のデータ コントラクト名は "ArrayOfint" です。Object のデータ コントラクト名は "anyType" であることに留意してください。したがって、ArrayList のような非ジェネリック リストのデータ コントラクト名は、"ArrayOfanyType" になります。

Name を使用してオーバーライドしていない場合、ディクショナリ コレクション データ コントラクトの既定の名前は、文字列 "ArrayOfKeyValueOf"、キーの型のデータ コントラクト名、および値の型のデータ コントラクト名をこの順番で組み合わせたものです。たとえば、String と Integer のジェネリック ディクショナリのデータ コントラクト名は、"ArrayOfKeyValueOfstringint" になります。また、キーの型と値の型がいずれもプリミティブ型ではない場合は、キーおよび値の型のデータ コントラクト名前空間の名前空間ハッシュが名前に付加されます。名前空間ハッシュ詳細情報、「データ コントラクト名」を参照してください。

各ディクショナリ コレクション データ コントラクトには、ディクショナリの 1 エントリを表すコンパニオン データ コントラクトが存在します。コンパニオン データ コントラクトの名前は、"ArrayOf" プレフィックスを除いたディクショナリ データ コントラクトの名前と同じです。また、名前空間は、ディクショナリ データ コントラクトの名前空間と同じです。たとえば、"ArrayOfKeyValueOfstringint" ディクショナリ データ コントラクトの場合、"KeyValueofstringint" データ コントラクトがディクショナリの 1 エントリを表します。次のセクションで説明するように、このデータ コントラクトの名前は、ItemName プロパティを使用してカスタマイズできます。

データ コントラクト名」に記載されたジェネリック型の名前付け規則は、コレクション型にすべて適用されます。つまり、Name 内の中かっこを使用してジェネリック型パラメーターを示すことができます。ただし、かっこ内の数値は、コレクションに含まれる型ではなく、ジェネリック パラメーターを指すことに注意してください。

コレクションのカスタマイズ

CollectionDataContractAttribute 属性を次のように使用することは禁止されています。次のように使用すると、InvalidDataContractException 例外が発生します。

  • CollectionDataContractAttribute 属性が既に適用されている型、またはその派生型のいずれかに DataContractAttribute 属性を適用する。

  • IXmlSerializable インターフェイスを実装する型に CollectionDataContractAttribute 属性を適用する。

  • コレクション型以外の型に CollectionDataContractAttribute 属性を適用する。

  • ディクショナリ型以外の型に適用された CollectionDataContractAttribute 属性で、KeyName または ValueName の設定を試みる。

ポリモーフィズム規則

既に説明したように、CollectionDataContractAttribute 属性を使用してコレクションをカスタマイズすると、コレクションの可換性を妨げることがあります。カスタマイズされた 2 つのコレクション型を等価と見なすことができるのは、それらの名前、名前空間、項目名、およびキーと値の名前 (ディクショナリ コレクションの場合) が一致する場合だけです。

カスタマイズが原因で、あるコレクション データ コントラクトを使用する必要があるところに、誤って別のコレクション データ コントラクトが使用される可能性があります。このような状況は回避する必要があります。次の型を参照してください。

[DataContract] public class Student {
    [DataMember] public string name;
    [DataMember] public IList<int> testMarks;
}
public class Marks1 : List<int> {}
[CollectionDataContract(ItemName="mark")]
public class Marks2 : List<int> {}

この場合、Marks1 のインスタンスは testMarks に割り当てることができます。ただし、Marks2 は使用しないようにする必要があります。そのデータ コントラクトは、IList<int> データ コントラクトと等価とは見なされないからです。このデータ コントラクト名は、"ArrayOfint" ではなく "Marks2" であり、反復される要素名は、"<int>" ではなく "<mark>" になります。

以下の表に、コレクションのポリモーフィックな割り当てに適用される規則を示します。

宣言された型 カスタマイズされていないコレクションの割り当て カスタマイズされたコレクションの割り当て

オブジェクト

コントラクト名はシリアル化されます。

コントラクト名はシリアル化されます。

カスタマイズが使用されます。

コレクション インターフェイス

コントラクト名はシリアル化されません。

コントラクト名はシリアル化されません。

カスタマイズは使用されません*。

カスタマイズされていないコレクション

コントラクト名はシリアル化されません。

コントラクト名はシリアル化されます。

カスタマイズが使用されます**。

カスタマイズされたコレクション

コントラクト名はシリアル化されます。カスタマイズは使用されません**。

コントラクト名はシリアル化されます。

割り当てられた型のカスタマイズが使用されます**。

* NetDataContractSerializer クラスでは、このケースでカスタマイズが使用されます。また、NetDataContractSerializer クラスは、このケースで実際の型名もシリアル化するため、逆シリアル化も予期されたとおりに機能します。

** これらのケースでは、スキーマが無効なインスタンスが生成されます。したがって、これらのケースは避けるようにしてください。

コントラクト名をシリアル化する場合、割り当てるコレクション型が既知の型リストに含まれている必要があります。逆に言うと、名前をシリアル化しない場合は、既知の型リストに型を追加する必要はありません。

派生型の配列は、基本型の配列に代入できます。この場合、反復される各要素について、派生型のコントラクト名がシリアル化されます。たとえば、Book 型が LibraryItem 型から派生している場合、Book の配列を LibraryItem の配列に代入できます。これは、他のコレクション型には適用されません。たとえば、Generic List of BookGeneric List of LibraryItem に代入することはできません。ただし、Book インスタンスを含む Generic List of LibraryItem を代入することはできます。配列の場合も、配列ではない場合も、Book は既知の型リストに含まれている必要があります。

コレクションとオブジェクト参照の保存

シリアライザーがオブジェクト参照を保存するモードで機能している場合、オブジェクト参照の保存はコレクションにも適用されます。具体的には、コレクション全体と、コレクションに含まれる個々の項目の両方のオブジェクト ID が保存されます。ディクショナリの場合、キーと値のペア オブジェクトと、個々のキー オブジェクトおよび値オブジェクトのオブジェクト ID が保存されます。

参照

リファレンス

CollectionDataContractAttribute