コレクションは、特定の種類の項目の一覧です。 .NET Framework では、このようなリストは、配列またはその他のさまざまな型 (ジェネリック リスト、ジェネリック BindingList<T>、 StringCollection、 ArrayList) を使用して表すことができます。 たとえば、コレクションは、特定の顧客のアドレスの一覧を保持できます。 これらのコレクションは、実際の型に関係なく、 リスト コレクションと呼ばれます。
1 つの項目 ("キー") と別の項目 ("値") の間の関連付けを表す特別な形式のコレクションが存在します。 .NET Framework では、これらは Hashtable やジェネリック ディクショナリなどの型によって表されます。 たとえば、関連付けコレクションでは、市区町村 ("key") を人口 ("value") にマップできます。 これらのコレクションは、実際の型に関係なく、 ディクショナリ コレクションと呼ばれます。
コレクションは、データ コントラクト モデルで特別な処理を受けます。
配列やジェネリック コレクションなど、 IEnumerable インターフェイスを実装する型はコレクションとして認識されます。 そのうち、 IDictionary インターフェイスまたはジェネリック IDictionary<TKey,TValue> インターフェイスを実装する型はディクショナリ コレクションです。それ以外はすべてリスト コレクションです。
Add
とパラメーターなしのコンストラクターと呼ばれるメソッドを持つなど、コレクション型に関する追加の要件については、次のセクションで詳しく説明します。 これにより、コレクション型をシリアル化および逆シリアル化できます。 つまり、ジェネリック ReadOnlyCollection<T> など、一部のコレクションは直接サポートされません (パラメーターなしのコンストラクターがないため)。 ただし、これらの制限を回避する方法については、このトピックで後述する「コレクション インターフェイス型とコレクション Read-Only の使用」を参照してください。
コレクションに含まれる型は、データ コントラクト型であるか、それ以外の場合はシリアル化可能である必要があります。 詳細については、「 データ コントラクト シリアライザーでサポートされる型」を参照してください。
有効なコレクションとは何か、何が有効なコレクションと見なされないのか、およびコレクションのシリアル化方法の詳細については、このトピックの「高度なコレクション規則」セクションのコレクションのシリアル化に関する情報を参照してください。
交換可能なコレクション
同じ型のすべてのリスト コレクションは、同じデータ コントラクトを持つと見なされます (このトピックで後述するように、 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;
}
<DataContract(Name:="PurchaseOrder")>
Public Class PurchaseOrder1
<DataMember()>
Public customerName As String
<DataMember()>
Public items As Collection(Of Item)
<DataMember()>
Public comments() As String
End Class
<DataContract(Name:="PurchaseOrder")>
Public Class PurchaseOrder2
<DataMember()>
Public customerName As String
<DataMember()>
Public items As List(Of Item)
<DataMember()>
Public comments As BindingList(Of String)
End Class
どちらのデータ コントラクトも、次のコードのような 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
型のジェネリック コレクションと同じデータ コントラクトを持つと見なされます。 (たとえば、Object
のArrayListと汎用List<T>のデータ コントラクトは同じです)。
コレクション インターフェイスの種類とコレクション Read-Only の使用
コレクション インターフェイス型 (IEnumerable、 IDictionary、ジェネリック IDictionary<TKey,TValue>、またはこれらのインターフェイスから派生したインターフェイス) も、コレクション データ コントラクトを持つと見なされます。これは、実際のコレクション型のコレクション データ コントラクトと同等です。 したがって、シリアル化される型をコレクション インターフェイス型として宣言することができ、結果は実際のコレクション型が使用された場合と同じです。 たとえば、次のデータ コントラクトは同等です。
[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;
}
<DataContract(Name:="Customer")>
Public Class Customer1
<DataMember()>
Public customerName As String
<DataMember()>
Public addresses As Collection(Of Address)
End Class
<DataContract(Name:="Customer")>
Public Class Customer2
<DataMember()>
Public customerName As String
<DataMember()>
Public addresses As ICollection(Of Address)
End Class
シリアル化中、宣言された型がインターフェイスの場合、実際に使用されるインスタンス型は、そのインターフェイスを実装する任意の型にすることができます。 前に説明した制限 (パラメーターなしのコンストラクターと Add
メソッドを持つ) は適用されません。 たとえば、ジェネリック ReadOnlyCollection<T>型のデータ メンバーを直接宣言できない場合でも、Customer2 のアドレスを Address の汎用ReadOnlyCollection<T>のインスタンスに設定できます。
逆シリアル化中に、宣言された型がインターフェイスである場合、シリアル化エンジンは宣言されたインターフェイスを実装する型を選択し、型がインスタンス化されます。 既知の型メカニズム ( データ コントラクトの既知の型に関するページで説明) は、ここでは効果がありません。型の選択は WCF に組み込まれています。
コレクション型のカスタマイズ
CollectionDataContractAttribute属性を使用してコレクション型をカスタマイズできます。この属性にはいくつかの用途があります。
コレクション型をカスタマイズするとコレクションの互換性が損なわれるので、通常は可能な限りこの属性を適用しないことをお勧めします。 この問題の詳細については、このトピックで後述する「高度なコレクション規則」セクションを参照してください。
コレクション データ コントラクトの名前付け
コレクション型の名前付け規則は、「 データ コントラクト名」で説明されているように、通常のデータ コントラクト型に名前を付ける規則と似ていますが、いくつかの重要な違いがあります。
CollectionDataContractAttribute属性は、DataContractAttribute属性ではなく、名前をカスタマイズするために使用されます。 CollectionDataContractAttribute属性には、
Name
プロパティとNamespace
プロパティもあります。CollectionDataContractAttribute属性が適用されていない場合、コレクション型の既定の名前と名前空間は、コレクション内に含まれる型の名前と名前空間によって異なります。 コレクション型自体の名前と名前空間の影響を受けません。 例については、次の種類を参照してください。
public CustomerList1 : Collection<string> {} public StringList1 : Collection<string> {}
どちらの型のデータ コントラクト名も "ArrayOfstring" であり、"CustomerList1" または "StringList1" ではありません。 つまり、ルート レベルでこれらの型のいずれかをシリアル化すると、次のコードのような XML が生成されます。
<ArrayOfstring>
<string>...</string>
<string>...</string>
<string>...</string>
...
</ArrayOfstring>
この名前付け規則は、文字列のリストを表すカスタマイズされていない型が同じデータ コントラクトと XML 表現を持っていることを確認するために選択されました。 これにより、コレクションの互換性が可能になります。 この例では、CustomerList1 と StringList1 は完全に交換可能です。
ただし、 CollectionDataContractAttribute 属性が適用されると、属性にプロパティが設定されていない場合でも、コレクションはカスタマイズされたコレクション データ コントラクトになります。 コレクション データ コントラクトの名前と名前空間は、コレクションの種類自体によって異なります。 例については、次の種類を参照してください。
[CollectionDataContract]
public class CustomerList2 : Collection<string> {}
<CollectionDataContract()>
Public Class CustomerList2
Inherits Collection(Of String)
End Class
シリアル化すると、結果の XML は次のようになります。
<CustomerList2>
<string>...</string>
<string>...</string>
<string>...</string>
...
</CustomerList2>
これは、カスタマイズされていない型の XML 表現と同等ではなくなったことに注意してください。
Name
プロパティとNamespace
プロパティを使用して、名前付けをさらにカスタマイズできます。 次のクラスを参照してください。[CollectionDataContract(Name="cust_list")] public class CustomerList3 : Collection<string> {}
<CollectionDataContract(Name:="cust_list")> Public Class CustomerList3 Inherits Collection(Of String) End Class
結果の XML は次のようになります。
<cust_list>
<string>...</string>
<string>...</string>
<string>...</string>
...
</cust_list>
詳細については、このトピックで後述する「高度なコレクション規則」セクションを参照してください。
リスト コレクションの繰り返し要素名のカスタマイズ
リスト コレクションには、繰り返しエントリが含まれています。 通常、各繰り返しエントリは、コレクションに含まれる型のデータ コントラクト名に従って名前が付けられた要素として表されます。
CustomerList
の例では、コレクションには文字列が含まれていました。 文字列プリミティブ型のデータ コントラクト名は "string" であるため、繰り返し要素は "<string>" でした。
ただし、CollectionDataContractAttribute属性で ItemName プロパティを使用すると、この繰り返し要素名をカスタマイズできます。 例については、次の種類を参照してください。
[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string> {}
<CollectionDataContract(ItemName:="customer")>
Public Class CustomerList4
Inherits Collection(Of String)
End Class
結果の 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> { }
<CollectionDataContract(Name:="CountriesOrRegionsWithCapitals",
ItemName:="entry", KeyName:="countryorregion",
ValueName:="capital")>
Public Class CountriesOrRegionsWithCapitals2
Inherits Dictionary(Of String, String)
End Class
シリアル化すると、結果の 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 つだけであり、同等のコレクションはすべて同じデータ コントラクトを持ちます。 つまり、前の例では、同じスコープで既知の型に ArrayList と Array of Object
の両方を追加することはできません。 ここでも、これはコレクション以外の型の既知の型の動作と同じですが、コレクションについて理解することが特に重要です。
コレクションの内容には既知の型が必要な場合もあります。 たとえば、 ArrayList に実際に Type1
と Type2
のインスタンスが含まれている場合は、両方の型を既知の型に追加する必要があります。
次の例は、コレクションと既知の型を使用して適切に構築されたオブジェクト グラフを示しています。 実際のアプリケーションでは、通常、次のデータ メンバーを 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
}
<DataContract()>
Public Class Employee
<DataMember()>
Public name As String = "John Doe"
<DataMember()>
Public payrollRecord As Payroll
<DataMember()>
Public trainingRecord As Training
End Class
<DataContract(), KnownType(GetType(Integer())), KnownType(GetType(ArrayList))>
Public Class Payroll
<DataMember()>
Public salaryPayments As Object = New Integer(11) {}
'float[] not needed in known types because polymorphic assignment is to another collection type
<DataMember()>
Public stockAwards As IEnumerable(Of Single) = New Single(11) {}
<DataMember()>
Public otherPayments As Object = New ArrayList()
End Class
'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
<DataContract(), KnownType(GetType(List(Of Object))),
KnownType(GetType(InHouseTraining)),
KnownType(GetType(OutsideTraining))>
Public Class Training
<DataMember()>
Public training As Object = New List(Of Object)()
End Class
<DataContract()>
Public Class InHouseTraining
'code omitted…
End Class
<DataContract()>
Public Class OutsideTraining
'code omitted…
End Class
逆シリアル化時に、宣言された型がコレクション型の場合、実際に送信された型に関係なく、宣言された型がインスタンス化されます。 宣言された型がコレクション インターフェイスの場合、デシリアライザーは、既知の型に関係なくインスタンス化される型を選択します。
また、逆シリアル化時に、宣言された型がコレクション型ではなく、コレクション型が送信されている場合は、一致するコレクション型が既知の型リストから選択されます。 逆シリアル化時に、コレクション インターフェイス型を既知の型の一覧に追加できます。 この場合、逆シリアル化エンジンはインスタンス化する型を再度選択します。
コレクションと NetDataContractSerializer クラス
NetDataContractSerializer クラスが使用されている場合、配列ではないカスタマイズされていないコレクション型 (CollectionDataContractAttribute属性なし) は、特別な意味を失います。
SerializableAttribute属性でマークされたカスタマイズされていないコレクション型は、SerializableAttribute属性またはISerializable インターフェイス規則に従って、NetDataContractSerializer クラスによってシリアル化できます。
カスタマイズされたコレクション型、コレクション インターフェイス、および配列は、 NetDataContractSerializer クラスが使用されている場合でも、コレクションとして扱われます。
コレクションとスキーマ
同等のコレクションはすべて、XML スキーマ定義言語 (XSD) スキーマで同じ表現を持ちます。 このため、通常、生成されたクライアント コードでは、サーバー上のコレクション型と同じコレクション型は取得されません。 たとえば、サーバーは Integer データ メンバーの汎用 List<T> を持つデータ コントラクトを使用できますが、生成されたクライアント コードでは、同じデータ メンバーが整数の配列になる可能性があります。
ディクショナリ コレクションは、ディクショナリであることを示す WCF 固有のスキーマ注釈でマークされます。それ以外の場合、キーと値を持つエントリを含む単純なリストと区別できません。 データ コントラクト スキーマでのコレクションの表現方法の詳細については、「 データ コントラクト スキーマ リファレンス」を参照してください。
既定では、インポートされたコード内のカスタマイズされていないコレクションに対して型は生成されません。 リスト コレクション型のデータ メンバーは配列としてインポートされ、ディクショナリ コレクション型のデータ メンバーは汎用ディクショナリとしてインポートされます。
ただし、カスタマイズされたコレクションでは、個別の型が生成され、 CollectionDataContractAttribute 属性でマークされます。 (スキーマ内のカスタマイズされたコレクション型は、既定の名前空間、名前、繰り返し要素名、またはキー/値要素名を使用しない型です)。これらの型は、リスト型のジェネリック List<T> から派生した空の型で、ディクショナリ型の場合は汎用ディクショナリです。
たとえば、サーバー上に次の種類があるとします。
[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);
}
}
<DataContract()>
Public Class CountryOrRegion
<DataMember()>
Public officialLanguages As Collection(Of String)
<DataMember()>
Public holidays As List(Of DateTime)
<DataMember()>
Public cities As CityList
<DataMember()>
Public otherInfo As ArrayList
End Class
Public Class Person
Public Sub New(ByVal fName As String, ByVal lName As String)
Me.firstName = fName
Me.lastName = lName
End Sub
Public firstName As String
Public lastName As String
End Class
Public Class PeopleEnum
Implements IEnumerator
Public _people() As Person
' Enumerators are positioned before the first element
' until the first MoveNext() call.
Private position As Integer = -1
Public Sub New(ByVal list() As Person)
_people = list
End Sub
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
position += 1
Return position < _people.Length
End Function
Public Sub Reset() Implements IEnumerator.Reset
position = -1
End Sub
Public ReadOnly Property Current() As Object Implements IEnumerator.Current
Get
Try
Return _people(position)
Catch e1 As IndexOutOfRangeException
Throw New InvalidOperationException()
End Try
End Get
End Property
End Class
<CollectionDataContract(Name:="Cities",
ItemName:="city",
KeyName:="cityName",
ValueName:="population")>
Public Class CityList
Implements IDictionary(Of String, Integer), IEnumerable(Of System.Collections.Generic.KeyValuePair(Of String, Integer))
Private _people() As Person = Nothing
Public Function ContainsKey(ByVal s As String) As Boolean Implements IDictionary(Of String, Integer).ContainsKey
Return True
End Function
Public Function Contains(ByVal s As String) As Boolean
Return True
End Function
Public Function Contains(ByVal item As KeyValuePair(Of String, Integer)) As Boolean Implements IDictionary(Of String, Integer).Contains
Return (True)
End Function
Public Sub Add(ByVal key As String,
ByVal value As Integer) Implements IDictionary(Of String, Integer).Add
End Sub
Public Sub Add(ByVal keykValue As KeyValuePair(Of String, Integer)) Implements IDictionary(Of String, Integer).Add
End Sub
Public Function Remove(ByVal s As String) As Boolean Implements IDictionary(Of String, Integer).Remove
Return True
End Function
Public Function TryGetValue(ByVal d As String,
<System.Runtime.InteropServices.Out()> ByRef i As Integer) _
As Boolean Implements IDictionary(Of String, Integer).TryGetValue
i = 0
Return (True)
End Function
Public ReadOnly Property Keys() As ICollection(Of String) Implements IDictionary(Of String, Integer).Keys
Get
Return CType(New Stack(Of String)(), System.Collections.Generic.ICollection(Of String))
End Get
End Property
Default Public Property Item(ByVal s As String) As Integer Implements IDictionary(Of String, Integer).Item
Get
Return 0
End Get
Set(ByVal value As Integer)
End Set
End Property
Public ReadOnly Property Values() As ICollection(Of Integer) Implements IDictionary(Of String, Integer).Values
Get
Return CType(New Stack(Of String)(), System.Collections.Generic.ICollection(Of Integer))
End Get
End Property
Public Sub Clear() Implements IDictionary(Of String, Integer).Clear
End Sub
Public Sub CopyTo(ByVal array() As KeyValuePair(Of String, Integer),
ByVal index As Integer) Implements IDictionary(Of String, Integer).CopyTo
End Sub
Public Function Remove(ByVal item As KeyValuePair(Of String, Integer)) As Boolean Implements IDictionary(Of String, Integer).Remove
Return True
End Function
Public ReadOnly Property Count() As Integer Implements IDictionary(Of String, Integer).Count
Get
Return 0
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements IDictionary(Of String, Integer).IsReadOnly
Get
Return True
End Get
End Property
Private Function IEnumerable_GetEnumerator() As IEnumerator(Of KeyValuePair(Of String, Integer)) _
Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of String, Integer)).GetEnumerator
Return CType(New PeopleEnum(_people), IEnumerator(Of KeyValuePair(Of String, Integer)))
End Function
Public Function GetEnumerator() As IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Return New PeopleEnum(_people)
End Function
End Class
スキーマをエクスポートして再度インポートすると、生成されるクライアント コードは次のようになります (読みやすくするためにプロパティの代わりにフィールドが表示されます)。
[DataContract]
public class CountryOrRegion2
{
[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 Cities : Dictionary<string, int> { }
<DataContract()>
Public Class CountryOrRegion2
<DataMember()>
Public officialLanguages() As String
<DataMember()>
Public holidays() As DateTime
<DataMember()>
Public cities As Cities
<DataMember()>
Public otherInfo() As Object
End Class
<CollectionDataContract(ItemName:="city", KeyName:="cityName", ValueName:="population")>
Public Class Cities
Inherits Dictionary(Of String, Integer)
End Class
生成されたコードでは、既定の型とは異なる型を使用できます。 たとえば、データ メンバーをユーザー インターフェイス コンポーネントにバインドしやすくするために、通常の配列の代わりにジェネリック BindingList<T> を使用できます。
生成するコレクション型を選択するには、スキーマのインポート時にImportOptions オブジェクトのReferencedCollectionTypes プロパティに使用するコレクション型の一覧を渡します。 これらの型は、 参照されるコレクション型と呼ばれます。
ジェネリック型が参照されている場合は、完全に開いているジェネリックまたは完全に閉じたジェネリックである必要があります。
注
Svcutil.exe ツールを使用する場合、この参照は /collectionType コマンド ライン スイッチ (短縮形: /ct) を使用して実現できます。 /reference スイッチ (短い形式: /r) を使用して、参照先のコレクション型のアセンブリも指定する必要があることに注意してください。 型がジェネリックの場合は、バッククォートとジェネリック パラメーターの数が続く必要があります。 バッククォート (') は、単一引用符 (') 文字と混同しないでください。 /collectionType スイッチを複数回使用して、複数の参照先コレクション型を指定できます。
たとえば、すべてのリストをジェネリック List<T>としてインポートする場合などです。
svcutil.exe MyService.wsdl MyServiceSchema.xsd /r:C:\full_path_to_system_dll\System.dll /ct:System.Collections.Generic.List`1
コレクションをインポートすると、この参照先コレクション型の一覧がスキャンされ、データ メンバー型 (カスタマイズされていないコレクションの場合) または派生元の基本型 (カスタマイズされたコレクションの場合) として、最適に一致するコレクションが見つかった場合に使用されます。 辞書は辞書に対してのみ照合され、リストはリストと照合されます。
たとえば、ジェネリック BindingList<T> を追加し、参照される型の一覧に Hashtable した場合、前の例で生成されるクライアント コードは次のようになります。
[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 { }
<DataContract()>
Public Class CountryOrRegion3
<DataMember()>
Public officialLanguages As BindingList(Of String)
<DataMember()>
Public holidays As BindingList(Of DateTime)
<DataMember()>
Public cities As Cities
<DataMember()>
Public otherInfo As BindingList(Of Object)
End Class
<CollectionDataContract(ItemName:="city",
KeyName:="cityName",
ValueName:="population")>
Public Class Cities3
Inherits Hashtable
End Class
参照先のコレクション型の一部としてコレクション インターフェイス型を指定することはできますが、無効なコレクション型 ( Add
メソッドやパブリック コンストラクターがないコレクション型など) を指定することはできません。
閉じたジェネリックが最適な一致と見なされます。 (非ジェネリック型は、 Object
の閉じたジェネリックと同等と見なされます)。 たとえば、DateTimeのジェネリック List<T>、ジェネリック BindingList<T> (オープン ジェネリック)、および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> { }
<DataContract()>
Public Class CountryOrRegion4
<DataMember()>
Public officialLanguages() As String
<DataMember()>
Public holidays() As DateTime
<DataMember()>
Public cities As Cities
<DataMember()>
Public otherInfo() As Object
End Class
<CollectionDataContract(ItemName:="city",
KeyName:="cityName",
ValueName:="population")>
Public Class Cities4
Inherits Dictionary(Of String, Integer)
End Class
リスト コレクションの場合は、次の表のケースのみがサポートされます。
参照される型 | 参照される型によって実装されるインターフェイス | 例 | 次のように扱われる型: |
---|---|---|---|
非ジェネリックまたはクローズ ジェネリック (任意の数のパラメーター) | 非ジェネリック | MyType : IList 又は MyType<T> : IList where T= int |
Object のクローズ ジェネリック (例: IList<object> ) |
非ジェネリックジェネリックまたはクローズ ジェネリック (コレクション型と必ずしも一致しないパラメーターの任意の数) | Closed ジェネリック | MyType : IList<string> 又は MyType<T> : IList<string> where 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<T> (またはその派生インターフェイス) を複数回実装する場合、その型は有効な参照先コレクション型とは見なされず、無視されます。 これは、一部の実装が無効な場合や、オープン ジェネリックを使用している場合でも当てはまります。 たとえば、
int
のジェネリック IEnumerable<T>を実装する型、および T のジェネリック IEnumerable<T>は、型にint
を受け入れるAdd
メソッドがあるか、T 型のパラメーターを受け入れるAdd
メソッドであるか、またはその両方であるかに関係なく、int
またはその他の型の参照先のコレクションとして使用されることはありません。型がジェネリック コレクション インターフェイスと IListを実装する場合、ジェネリック コレクション インターフェイスが Object型の閉じたジェネリックでない限り、型は参照先のコレクション型として使用されません。
ディクショナリ コレクションの場合は、次の表のケースのみがサポートされます。
参照される型 | 参照される型によって実装されるインターフェイス | 例 | として扱われる型 |
---|---|---|---|
非ジェネリックまたはクローズ ジェネリック (任意の数のパラメーター) | IDictionary | MyType : IDictionary 又は MyType<T> : IDictionary where T=int |
Closed ジェネリック IDictionary<object,object> |
クローズド ジェネリック (任意の数のパラメーター) | IDictionary<TKey,TValue>クローズド |
MyType<T> : IDictionary<string, bool> where T=int |
クローズド ジェネリック (たとえば、 IDictionary<string,bool> ) |
クローズド ジェネリック (任意の数のパラメーター) | ジェネリック IDictionary<TKey,TValue>。いずれかのキーまたは値が閉じられ、もう一方が開き、型のいずれかのパラメーターが使用されます |
MyType<T,U,V> : IDictionary<string,V> where T=int , U=float ,V=bool 又は MyType<Z> : IDictionary<Z,bool> where Z=string |
Closed ジェネリック (たとえば、 IDictionary<string,bool> ) |
クローズド ジェネリック (任意の数のパラメーター) | ジェネリック IDictionary<TKey,TValue>、キーと値の両方が開き、それぞれが型のパラメーターのいずれかを使用します |
MyType<T,U,V> : IDictionary<V,U> ここで、T=int 、U=bool 、V=string |
クローズド ジェネリック (たとえば、 IDictionary<string,bool> ) |
ジェネリックを開く (2 つのパラメーター) | ジェネリック IDictionary<TKey,TValue>、開いている場合は、両方の型のジェネリック パラメーターが表示される順序で使用されます |
MyType<K,V> : IDictionary<K,V> 、K と V の両方が開いている |
ジェネリックを開く (たとえば、 IDictionary<K,V> ) |
型が IDictionaryIDictionary<TKey,TValue>とジェネリック の両方を実装している場合は、ジェネリック IDictionary<TKey,TValue> のみが考慮されます。
部分ジェネリック型の参照はサポートされていません。
たとえば、Integer
のジェネリック List<T>とInteger
のジェネリック コレクションの両方をReferencedCollectionTypesに追加することはできません。これは、スキーマで整数の一覧が見つかったときに使用するものを特定できないためです。 重複が検出されるのは、重複の問題を公開するスキーマに型がある場合のみです。 たとえば、インポートするスキーマに整数のリストが含まれていない場合は、Integer
の汎用List<T>とReferencedCollectionTypesのInteger
のジェネリック コレクションの両方を持つことができますが、どちらも効果がありません。
高度なコレクション規則
コレクションのシリアル化
シリアル化のコレクション規則の一覧を次に示します。
(コレクションのコレクションを持つ) コレクション型の組み合わせが可能です。 ジャグ配列はコレクションのコレクションとして扱われます。 多次元配列はサポートされていません。
バイト配列と XmlNode 配列は、コレクションではなくプリミティブとして扱われる特殊な配列型です。 バイト配列をシリアル化すると、バイトごとに個別の要素ではなく、Base64 でエンコードされたデータのチャンクを含む単一の XML 要素が生成されます。 XmlNodeの配列の処理方法の詳細については、「データ コントラクトの XML 型と ADO.NET 型」を参照してください。 もちろん、これらの特殊な型自体はコレクションに参加できます。バイト配列の配列は複数の XML 要素で、それぞれに Base64 でエンコードされたデータのチャンクが含まれます。
DataContractAttribute属性がコレクション型に適用されている場合、型はコレクションとしてではなく、通常のデータ コントラクト型として扱われます。
コレクション型が IXmlSerializable インターフェイスを実装する場合、型
myType:IList<string>, IXmlSerializable
指定すると、次の規則が適用されます。宣言された型が
IList<string>
場合、型はリストとしてシリアル化されます。宣言された型が
myType
場合は、IXmlSerializable
としてシリアル化されます。宣言された型が
IXmlSerializable
されている場合は、IXmlSerializable
としてシリアル化されますが、既知の型の一覧にmyType
を追加した場合に限られます。
コレクションは、次の表に示すメソッドを使用してシリアル化および逆シリアル化されます。
コレクション型の実装 | シリアル化で呼び出されるメソッド | 逆シリアル化時に呼び出されるメソッド |
---|---|---|
ジェネリック IDictionary<TKey,TValue> |
get_Keys 、get_Values |
汎用追加 |
IDictionary |
get_Keys 、get_Values |
Add |
ジェネリック IList<T> | 汎用 IList<T> インデクサー | 汎用追加 |
ジェネリック ICollection<T> | 列挙子 | 汎用追加 |
IList | IList インデクサ | Add |
ジェネリック IEnumerable<T> | GetEnumerator |
適切な型の 1 つのパラメーター (ジェネリック パラメーターの型またはその基本型) を受け取る、 Add 呼び出される非静的メソッド。 シリアル化と逆シリアル化の両方でコレクション型をコレクションとして扱うには、シリアライザーにこのようなメソッドが存在する必要があります。 |
IEnumerable (したがって、そこから派生する ICollection) | GetEnumerator |
Object 型の 1 つのパラメーターを受け取るAdd 呼び出される非静的メソッド。 シリアル化と逆シリアル化の両方でコレクション型をコレクションとして扱うには、シリアライザーにこのようなメソッドが存在する必要があります。 |
上の表は、コレクション インターフェイスを優先順位の降順で示しています。 つまり、たとえば、型が IList とジェネリック IEnumerable<T>の両方を実装する場合、コレクションは IList 規則に従ってシリアル化および逆シリアル化されます。
逆シリアル化時には、最初にパラメーターなしのコンストラクターを呼び出して型のインスタンスを作成することで、すべてのコレクションが逆シリアル化されます。これは、シリアル化と逆シリアル化の両方で、シリアライザーがコレクション型をコレクションとして扱うために存在する必要があります。
同じジェネリック コレクション インターフェイスが複数回実装されている場合 (たとえば、型が
Integer
のジェネリック ICollection<T>とStringのジェネリック ICollection<T>の両方を実装している場合)、優先順位の高いインターフェイスが見つからない場合、コレクションは有効なコレクションとして扱われません。コレクション型には、 SerializableAttribute 属性を適用し、 ISerializable インターフェイスを実装できます。 これらはどちらも無視されます。 ただし、型がコレクション型の要件を完全に満たしていない場合 (たとえば、
Add
メソッドが不足しています)、型はコレクション型とは見なされないため、 SerializableAttribute 属性と ISerializable インターフェイスを使用して、型をシリアル化できるかどうかを判断します。CollectionDataContractAttribute属性をコレクションに適用してカスタマイズすると、前のフォールバック メカニズムSerializableAttributeが削除されます。 代わりに、カスタマイズされたコレクションがコレクション型の要件を満たしていない場合は、 InvalidDataContractException 例外がスローされます。 例外文字列には、多くの場合、特定の型が有効なコレクションと見なされない理由 (
Add
メソッドなし、パラメーターなしのコンストラクターなしなど) を説明する情報が含まれているため、多くの場合、デバッグのために CollectionDataContractAttribute 属性を適用すると便利です。
コレクションの名前付け
コレクションの名前付け規則の一覧を次に示します。
名前空間を使用してオーバーライドされない限り、すべてのディクショナリ コレクション データ コントラクトの既定の名前空間と、プリミティブ型を含むリスト コレクション データ コントラクトの既定の名前空間は
http://schemas.microsoft.com/2003/10/Serialization/Arrays
されます。 組み込みの XSD 型だけでなく、char
、Timespan
、Guid
型にマップされる型は、この目的でプリミティブと見なされます。名前空間を使用してオーバーライドされない限り、非プリミティブ型を含むコレクション型の既定の名前空間は、コレクションに含まれる型のデータ コントラクト名前空間と同じです。
Name を使用してオーバーライドされない限り、リスト コレクション データ コントラクトの既定の名前は、文字列 "ArrayOf" とコレクションに含まれる型のデータ コントラクト名を組み合わせたものです。 たとえば、整数のジェネリック リストのデータ コントラクト名は "ArrayOfint" です。
Object
のデータ コントラクト名は "anyType" であるため、ArrayList のような非ジェネリック リストのデータ コントラクト名は "ArrayOfanyType" であることに注意してください。
Name
を使用してオーバーライドされない限り、ディクショナリ コレクション データ コントラクトの既定の名前は、文字列 "ArrayOfKeyValueOf" とキー型のデータ コントラクト名の後に値型のデータ コントラクト名を組み合わせたものです。 たとえば、文字列と整数の汎用ディクショナリのデータ コントラクト名は "ArrayOfKeyValueOfstringint" です。 さらに、キー型または値型がプリミティブ型でない場合は、キー型と値型のデータ コントラクト名前空間の名前空間ハッシュが名前に追加されます。 名前空間ハッシュの詳細については、「 データ コントラクト名」を参照してください。
各ディクショナリ コレクション データ コントラクトには、ディクショナリ内の 1 つのエントリを表すコンパニオン データ コントラクトがあります。 名前は、"ArrayOf" プレフィックスを除き、ディクショナリ データ コントラクトの名前と同じであり、その名前空間はディクショナリ データ コントラクトの場合と同じです。 たとえば、"ArrayOfKeyValueOfstringint" ディクショナリ データ コントラクトの場合、"KeyValueofstringint" データ コントラクトはディクショナリ内の 1 つのエントリを表します。 次のセクションで説明するように、 ItemName
プロパティを使用して、このデータ コントラクトの名前をカスタマイズできます。
データ コントラクト名の説明に従って、ジェネリック型の名前付け規則は、コレクション型に完全に適用されます。つまり、Name 内で中かっこを使用して、ジェネリック型パラメーターを示すことができます。 ただし、中かっこ内の数値は、コレクション内に含まれる型ではなく、ジェネリック パラメーターを参照します。
コレクションのカスタマイズ
CollectionDataContractAttribute属性の次の使用は禁止されており、InvalidDataContractException例外が発生します。
CollectionDataContractAttribute属性が適用されている型、またはその派生型のいずれかにDataContractAttribute属性を適用する。
IXmlSerializable インターフェイスを実装する型にCollectionDataContractAttribute属性を適用する。
コレクション以外の型に CollectionDataContractAttribute 属性を適用する。
非ディクショナリ型に適用されるCollectionDataContractAttribute属性に対してKeyNameまたはValueNameを設定しようとしています。
ポリモーフィズムルール
前述のように、 CollectionDataContractAttribute 属性を使用してコレクションをカスタマイズすると、コレクションの互換性が妨げられる可能性があります。 カスタマイズされた 2 つのコレクション型は、名前、名前空間、項目名、キーと値の名前 (ディクショナリ コレクションの場合) が一致する場合にのみ同等と見なすことができます。
カスタマイズにより、別のコレクション データ コントラクトが予期される場合に、誤って 1 つのコレクション データ コントラクトを使用する可能性があります。 これは避ける必要があります。 次の種類を参照してください。
[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> {}
<DataContract()>
Public Class Student
<DataMember()>
Public name As String
<DataMember()>
Public testMarks As IList(Of Integer)
End Class
Public Class Marks1
Inherits List(Of Integer)
End Class
<CollectionDataContract(ItemName:="mark")>
Public Class Marks2
Inherits List(Of Integer)
End Class
この場合、 Marks1
のインスタンスを testMarks
に割り当てることができます。 ただし、データ コントラクトはIList<int>
データ コントラクトと同等と見なされないため、Marks2
は使用しないでください。 データ コントラクト名は "ArrayOfint" ではなく "Marks2" で、繰り返し要素名は "<mark>" であり、"<int>" ではありません。
次の表の規則は、コレクションのポリモーフィックな割り当てに適用されます。
宣言された型 | カスタマイズされていないコレクションの割り当て | カスタマイズされたコレクションの割り当て |
---|---|---|
オブジェクト | コントラクト名がシリアル化されます。 | コントラクト名がシリアル化されます。 カスタマイズが使用されます。 |
コレクション インターフェイス | コントラクト名はシリアル化されません。 | コントラクト名はシリアル化されません。 カスタマイズは使用されません。* |
カスタマイズされていないコレクション | コントラクト名はシリアル化されません。 | コントラクト名がシリアル化されます。 カスタマイズが使用されます。** |
カスタマイズされたコレクション | コントラクト名がシリアル化されます。 カスタマイズは使用されません。** | コントラクト名がシリアル化されます。 割り当てられた型のカスタマイズが使用されます。** |
* NetDataContractSerializer クラスでは、この場合はカスタマイズが使用されます。 この場合、 NetDataContractSerializer クラスは実際の型名もシリアル化するため、逆シリアル化は想定どおりに動作します。
**このような場合は、スキーマが無効なインスタンスになるため、避ける必要があります。
コントラクト名がシリアル化される場合は、割り当てられたコレクション型が既知の型の一覧に含まれている必要があります。 逆も当てはまります。名前がシリアル化されていない場合は、既知の型リストに型を追加する必要はありません。
派生型の配列は、基本型の配列に割り当てることができます。 この場合、派生型のコントラクト名は、繰り返し要素ごとにシリアル化されます。 たとえば、型Book
LibraryItem
から派生した型の場合、Book
の配列をLibraryItem
の配列に割り当てることができます。 これは、他のコレクション型には適用されません。 たとえば、 Generic List of Book
を Generic List of LibraryItem
に割り当てることはできません。 ただし、Book
インスタンスを含むGeneric List of LibraryItem
を割り当てることができます。 配列と非配列のどちらの場合も、 Book
は既知の型リストに含まれている必要があります。
コレクションとオブジェクト参照の保持
シリアライザーがオブジェクト参照を保持するモードで機能する場合、オブジェクト参照の保持もコレクションに適用されます。 具体的には、コレクション全体とコレクションに含まれる個々の項目の両方に対してオブジェクト ID が保持されます。 ディクショナリの場合、オブジェクト ID は、キー/値ペア オブジェクトと個々のキーオブジェクトと値オブジェクトの両方に対して保持されます。