Typy kolekcji w kontraktach danych
Kolekcja to lista elementów określonego typu. W programie .NET Framework takie listy mogą być reprezentowane przy użyciu tablic lub różnych innych typów (Lista ogólna, Ogólna BindingList<T>, , StringCollectionlub ArrayList). Na przykład kolekcja może zawierać listę adresów dla danego klienta. Te kolekcje są nazywane kolekcjami list, niezależnie od ich rzeczywistego typu.
Istnieje specjalna forma kolekcji, która reprezentuje skojarzenie między jednym elementem (kluczem) a drugim ("wartość"). W programie .NET Framework są one reprezentowane przez typy, takie jak Hashtable i słownik ogólny. Na przykład kolekcja skojarzeń może mapować miasto ("klucz") na jego populację ("wartość"). Te kolekcje są nazywane kolekcjami słowników, niezależnie od ich rzeczywistego typu.
Kolekcje otrzymują specjalne traktowanie w modelu kontraktu danych.
Typy implementujące IEnumerable interfejs, w tym tablice i kolekcje ogólne, są rozpoznawane jako kolekcje. Z tych typów, które implementują IDictionary interfejsy ogólne lub IDictionary<TKey,TValue> to kolekcje słowników; wszystkie inne to kolekcje list.
Dodatkowe wymagania dotyczące typów kolekcji, takich jak wywoływanie metody Add
i konstruktor bez parametrów, zostały szczegółowo omówione w poniższych sekcjach. Dzięki temu typy kolekcji mogą być serializowane i deserializowane. Oznacza to, że niektóre kolekcje nie są bezpośrednio obsługiwane, takie jak Generic ReadOnlyCollection<T> (ponieważ nie ma konstruktora bez parametrów). Aby uzyskać jednak informacje na temat obejścia tych ograniczeń, zobacz sekcję "Using Collection Interface Types and Read-Only Collections" (Używanie typów interfejsów kolekcji i kolekcji tylko do odczytu) w dalszej części tego tematu.
Typy zawarte w kolekcjach muszą być typami kontraktów danych lub mogą być serializowalne. Aby uzyskać więcej informacji, zobacz Typy obsługiwane przez serializator kontraktu danych.
Aby uzyskać więcej informacji o tym, co to jest i co nie jest uważane za prawidłową kolekcję, a także o tym, jak kolekcje są serializowane, zobacz informacje na temat serializacji kolekcji w sekcji "Zaawansowane reguły kolekcji" w tym temacie.
Kolekcje z możliwością wymiany
Wszystkie kolekcje list tego samego typu są uważane za takie same kontrakty danych (chyba że są dostosowane przy użyciu atrybutu, jak opisano w dalszej części tego tematu CollectionDataContractAttribute ). Na przykład następujące kontrakty danych są równoważne.
[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
Oba kontrakty danych powodują kod XML podobny do poniższego.
<PurchaseOrder>
<customerName>...</customerName>
<items>
<Item>...</Item>
<Item>...</Item>
<Item>...</Item>
...
</items>
<comments>
<string>...</string>
<string>...</string>
<string>...</string>
...
</comments>
</PurchaseOrder>
Możliwość wymiany kolekcji umożliwia na przykład użycie typu kolekcji zoptymalizowanego pod kątem wydajności na serwerze i typu kolekcji przeznaczonego do powiązania ze składnikami interfejsu użytkownika na kliencie.
Podobnie jak w przypadku kolekcji list, wszystkie kolekcje słowników, które mają ten sam klucz i typy wartości, są uważane za takie same kontrakty danych (chyba że zostały dostosowane przez CollectionDataContractAttribute atrybut).
Tylko typ kontraktu danych ma znaczenie, jeśli chodzi o równoważność kolekcji, a nie typy platformy .NET. Oznacza to, że kolekcja Typu1 jest uważana za równoważną kolekcji Type2, jeśli type1 i Type2 mają równoważne kontrakty danych.
Kolekcje nieogólne są uważane za takie same kontrakty danych jak kolekcje ogólne typu Object
. (Na przykład kontrakty danych dla ArrayList i ogólne List<T> są Object
takie same).
Używanie typów interfejsów kolekcji i kolekcji tylko do odczytu
Typy interfejsów kolekcji (IEnumerable, IDictionaryogólne IDictionary<TKey,TValue>lub interfejsy pochodzące z tych interfejsów) są również traktowane jako mające kontrakty danych zbierania, równoważne kontraktom danych zbierania dla rzeczywistych typów kolekcji. W związku z tym można zadeklarować typ serializacji jako typ interfejsu kolekcji, a wyniki są takie same, jak w przypadku użycia rzeczywistego typu kolekcji. Na przykład następujące kontrakty danych są równoważne.
[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
Podczas serializacji, gdy zadeklarowany typ jest interfejsem, rzeczywisty typ wystąpienia może być dowolnym typem implementujący ten interfejs. Ograniczenia omówione wcześniej (o konstruktorze bez parametrów i metodzie Add
) nie mają zastosowania. Można na przykład ustawić adresy w customer2 na wystąpienie wartości Generic ReadOnlyCollection<T> of Address, mimo że nie można bezpośrednio zadeklarować elementu członkowskiego danych typu Generic ReadOnlyCollection<T>.
Podczas deserializacji, gdy zadeklarowany typ jest interfejsem, aparat serializacji wybiera typ implementujący zadeklarowany interfejs, a typ jest tworzone. Znany mechanizm typów (opisany w artykule Znane typy kontraktów danych) nie ma tutaj wpływu; wybór typu jest wbudowany w usługę WCF.
Dostosowywanie typów kolekcji
Typy kolekcji można dostosować przy użyciu atrybutu CollectionDataContractAttribute , który ma kilka zastosowań.
Należy pamiętać, że dostosowywanie typów kolekcji narusza możliwość wymiany kolekcji, dlatego zaleca się unikanie stosowania tego atrybutu zawsze, gdy jest to możliwe. Aby uzyskać więcej informacji na temat tego problemu, zobacz sekcję "Zaawansowane reguły zbierania" w dalszej części tego tematu.
Nazewnictwo kontraktu danych zbierania
Reguły nazewnictwa typów kolekcji są podobne do tych w przypadku nazywania zwykłych typów kontraktów danych, zgodnie z opisem w temacie Nazwy kontraktów danych, chociaż istnieją pewne ważne różnice:
Atrybut CollectionDataContractAttribute służy do dostosowywania nazwy zamiast atrybutu DataContractAttribute . Atrybut CollectionDataContractAttribute ma
Name
również właściwości iNamespace
.CollectionDataContractAttribute Jeśli atrybut nie jest stosowany, domyślna nazwa i przestrzeń nazw typów kolekcji zależą od nazw i przestrzeni nazw typów zawartych w kolekcji. Nie mają one wpływu na nazwę i przestrzeń nazw samego typu kolekcji. Aby zapoznać się z przykładem, zobacz następujące typy.
public CustomerList1 : Collection<string> {} public StringList1 : Collection<string> {}
Nazwa kontraktu danych obu typów to "ArrayOfstring", a nie "CustomerList1" lub "StringList1". Oznacza to, że serializowanie dowolnego z tych typów na poziomie głównym daje kod XML podobny do poniższego kodu.
<ArrayOfstring>
<string>...</string>
<string>...</string>
<string>...</string>
...
</ArrayOfstring>
Ta reguła nazewnictwa została wybrana w celu upewnienia się, że dowolny typ, który reprezentuje listę ciągów, ma ten sam kontrakt danych i reprezentację XML. Umożliwia to wymianę kolekcji. W tym przykładzie listy CustomerList1 i StringList1 są całkowicie wymienne.
Jednak po zastosowaniu atrybutu CollectionDataContractAttribute kolekcja staje się dostosowanym kontraktem danych zbierania, nawet jeśli żadne właściwości nie są ustawione na atrybucie. Nazwa i przestrzeń nazw kontraktu danych kolekcji zależą od samego typu kolekcji. Aby zapoznać się z przykładem, zobacz następujący typ.
[CollectionDataContract]
public class CustomerList2 : Collection<string> {}
<CollectionDataContract()>
Public Class CustomerList2
Inherits Collection(Of String)
End Class
Po serializacji wynikowy kod XML jest podobny do poniższego.
<CustomerList2>
<string>...</string>
<string>...</string>
<string>...</string>
...
</CustomerList2>
Zwróć uwagę, że nie jest to już odpowiednik reprezentacji XML typów, które nie są dostosowane.
Możesz użyć właściwości
Name
iNamespace
, aby jeszcze bardziej dostosować nazewnictwo. Zobacz następującą klasę.[CollectionDataContract(Name="cust_list")] public class CustomerList3 : Collection<string> {}
<CollectionDataContract(Name:="cust_list")> Public Class CustomerList3 Inherits Collection(Of String) End Class
Wynikowy kod XML jest podobny do poniższego.
<cust_list>
<string>...</string>
<string>...</string>
<string>...</string>
...
</cust_list>
Aby uzyskać więcej informacji, zobacz sekcję "Zaawansowane reguły zbierania" w dalszej części tego tematu.
Dostosowywanie nazwy powtarzanego elementu w kolekcjach list
Kolekcje list zawierające powtarzające się wpisy. Zwykle każdy powtarzający się wpis jest reprezentowany jako element o nazwie zgodnie z nazwą kontraktu danych typu zawartego w kolekcji.
CustomerList
W przykładach kolekcje zawierały ciągi. Nazwa kontraktu danych dla typu pierwotnego ciągu to "string", więc powtarzający się element to "<string>".
Jednak przy użyciu właściwości atrybutu ItemNameCollectionDataContractAttribute można dostosować tę powtarzającą nazwę elementu. Aby zapoznać się z przykładem, zobacz następujący typ.
[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string> {}
<CollectionDataContract(ItemName:="customer")>
Public Class CustomerList4
Inherits Collection(Of String)
End Class
Wynikowy kod XML jest podobny do poniższego.
<CustomerList4>
<customer>...</customer>
<customer>...</customer>
<customer>...</customer>
...
</CustomerList4>
Przestrzeń nazw powtarzającego się elementu jest zawsze taka sama jak przestrzeń nazw kontraktu danych kolekcji, którą można dostosować przy użyciu właściwości, zgodnie z Namespace
wcześniejszym opisem.
Dostosowywanie kolekcji słowników
Kolekcje słowników są zasadniczo listami wpisów, gdzie każdy wpis ma klucz, po którym następuje wartość. Podobnie jak w przypadku zwykłych list, można zmienić nazwę elementu odpowiadającą powtarzającemu się elemencie ItemName za pomocą właściwości .
Ponadto można zmienić nazwy elementów reprezentujących klucz i wartość przy użyciu KeyName właściwości i ValueName . Przestrzenie nazw dla tych elementów są takie same jak przestrzeń nazw kontraktu danych kolekcji.
Aby zapoznać się z przykładem, zobacz następujący typ.
[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
Po serializacji wynikowy kod XML jest podobny do poniższego.
<CountriesOrRegionsWithCapitals>
<entry>
<countryorregion>USA</countryorregion>
<capital>Washington</capital>
</entry>
<entry>
<countryorregion>France</countryorregion>
<capital>Paris</capital>
</entry>
...
</CountriesOrRegionsWithCapitals>
Aby uzyskać więcej informacji na temat kolekcji słowników, zobacz sekcję "Zaawansowane reguły kolekcji" w dalszej części tego tematu.
Kolekcje i znane typy
Nie trzeba dodawać typów kolekcji do znanych typów w przypadku użycia polimorficznie zamiast innych kolekcji lub interfejsów kolekcji. Jeśli na przykład zadeklarowasz element członkowski danych typu IEnumerable i użyjesz go do wysłania wystąpienia ArrayListklasy , nie musisz dodawać ArrayList do znanych typów.
W przypadku używania kolekcji polimorficznie zamiast typów niezwiązanych z kolekcjami należy je dodać do znanych typów. Jeśli na przykład zadeklarujesz element członkowski danych typu Object
i użyjesz go do wysłania wystąpienia ArrayListklasy , dodaj ArrayList do znanych typów.
Nie pozwala to serializować żadnych równoważnych kolekcji polimorficznie. Na przykład podczas dodawania ArrayList do listy znanych typów w poprzednim przykładzie nie pozwala to na przypisanie Array of Object
klasy, mimo że ma równoważną umowę danych. Nie różni się to od zwykłego zachowania znanych typów serializacji dla typów niezwiązanych z kolekcjami, ale szczególnie ważne jest, aby zrozumieć w przypadku kolekcji, ponieważ jest to bardzo powszechne, aby kolekcje stały się równoważne.
Podczas serializacji tylko jeden typ może być znany w dowolnym zakresie dla danego kontraktu danych, a wszystkie kolekcje równoważne mają te same kontrakty danych. Oznacza to, że w poprzednim przykładzie nie można dodać zarówno typów ArrayList , jak i Array of Object
do znanych typów w tym samym zakresie. Ponownie jest to równoważne znanym typom zachowania dla typów niezwiązanych z kolekcjami, ale szczególnie ważne jest zrozumienie dla kolekcji.
Znane typy mogą być również wymagane dla zawartości kolekcji. Jeśli na przykład element ArrayList zawiera wystąpienia Type1
elementów i Type2
, oba te typy powinny zostać dodane do znanych typów.
W poniższym przykładzie przedstawiono prawidłowo skonstruowany graf obiektu przy użyciu kolekcji i znanych typów. Przykład jest nieco contrived, ponieważ w rzeczywistej aplikacji zwykle nie definiuje się następujących elementów członkowskich danych jako Object
, a tym samym nie ma żadnych znanych problemów z typem/polimorfizmem.
[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
W przypadku deserializacji, jeśli zadeklarowany typ jest typem kolekcji, zadeklarowany typ jest tworzone niezależnie od typu, który został rzeczywiście wysłany. Jeśli zadeklarowany typ jest interfejsem kolekcji, deserializator wybiera typ do utworzenia wystąpienia bez względu na znane typy.
Ponadto w przypadku deserializacji, jeśli zadeklarowany typ nie jest typem kolekcji, ale jest wysyłany typ kolekcji, pasujący typ kolekcji jest wybierany z listy znanych typów. Istnieje możliwość dodania typów interfejsów kolekcji do listy znanych typów deserializacji. W tym przypadku aparat deserializacji ponownie wybiera typ do utworzenia wystąpienia.
Kolekcje i klasa NetDataContractSerializer
NetDataContractSerializer Gdy klasa jest używana, nieukonfigurowane typy kolekcji (bez atrybutuCollectionDataContractAttribute), które nie są tablicami, tracą specjalne znaczenie.
Nieukonfigurowane typy kolekcji oznaczone atrybutem SerializableAttribute mogą być nadal serializowane przez klasę NetDataContractSerializer zgodnie z atrybutem SerializableAttribute lub regułami interfejsu ISerializable .
Niestandardowe typy kolekcji, interfejsy kolekcji i tablice są nadal traktowane jako kolekcje, nawet jeśli NetDataContractSerializer klasa jest używana.
Kolekcje i schemat
Wszystkie równoważne kolekcje mają taką samą reprezentację w schemacie języka definicji schematu XML (XSD). W związku z tym zwykle nie uzyskujesz tego samego typu kolekcji w wygenerowany kod klienta co ten na serwerze. Na przykład serwer może używać kontraktu danych z ogólnym elementem List<T> członkowskim danych całkowitych, ale w wygenerowanym kodzie klienta ten sam element członkowski danych może stać się tablicą liczb całkowitych.
Kolekcje słowników są oznaczone adnotacją schematu specyficzną dla programu WCF, która wskazuje, że są słownikami; w przeciwnym razie są one nie do odróżnienia od prostych list, które zawierają wpisy z kluczem i wartością. Aby uzyskać dokładny opis sposobu przedstawiania kolekcji w schemacie kontraktu danych, zobacz Dokumentacja schematu kontraktu danych.
Domyślnie typy nie są generowane dla kolekcji niestandardowych w importowanym kodzie. Elementy członkowskie listy typów kolekcji są importowane jako tablice, a elementy członkowskie danych typów kolekcji słowników są importowane jako słownik ogólny.
Jednak w przypadku niestandardowych kolekcji generowane są oddzielne typy oznaczone atrybutem CollectionDataContractAttribute . (Niestandardowy typ kolekcji w schemacie jest taki, który nie używa domyślnej przestrzeni nazw, nazwy, powtarzającej się nazwy elementu lub nazw elementów klucz/wartość). Te typy są pustymi typami pochodzącymi z typów list ogólnych List<T> i słownika ogólnego dla typów słowników.
Na przykład na serwerze mogą istnieć następujące typy.
[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
Po ponownym wyeksportowaniu i zaimportowaniu schematu wygenerowany kod klienta jest podobny do poniższego (pola są wyświetlane zamiast właściwości w celu ułatwienia odczytywania).
[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
Możesz użyć różnych typów w wygenerowanych kodzie niż domyślne. Na przykład możesz użyć funkcji Generic BindingList<T> zamiast zwykłych tablic dla członków danych, aby ułatwić powiązanie ich ze składnikami interfejsu użytkownika.
Aby wybrać typy kolekcji do wygenerowania, przekaż listę typów kolekcji, których chcesz użyć do ReferencedCollectionTypes właściwości ImportOptions obiektu podczas importowania schematu. Te typy są nazywane typami kolekcji przywoływanych.
Gdy odwołania do typów ogólnych są przywoływane, muszą być w pełni otwarte lub w pełni zamknięte typy ogólne.
Uwaga
W przypadku korzystania z narzędzia Svcutil.exe można wykonać to odwołanie za pomocą przełącznika wiersza polecenia /collectionType (krótki formularz: /ct). Należy pamiętać, że należy również określić zestaw dla odwołanych typów kolekcji przy użyciu przełącznika /reference (krótki formularz: /r). Jeśli typ jest ogólny, musi być zgodny z cudzysłowem wstecznym i liczbą parametrów ogólnych. Cudzysłów (') nie należy mylić z znakiem pojedynczego cudzysłowu ('). Przy użyciu przełącznika /collectionType można określić wiele przywołynych typów kolekcji więcej niż raz.
Na przykład, aby spowodować zaimportowanie wszystkich list jako ogólnych List<T>.
svcutil.exe MyService.wsdl MyServiceSchema.xsd /r:C:\full_path_to_system_dll\System.dll /ct:System.Collections.Generic.List`1
Podczas importowania dowolnej kolekcji ta lista przywoływane typy kolekcji jest skanowana, a najlepiej pasująca kolekcja jest używana, jeśli zostanie znaleziona, jako typ elementu członkowskiego danych (w przypadku kolekcji nieustosowanych) lub jako typ podstawowy do uzyskania (dla niestandardowych kolekcji). Słowniki są dopasowywane tylko do słowników, podczas gdy listy są dopasowywane do list.
Jeśli na przykład dodasz typ ogólny BindingList<T> i Hashtable do listy typów, do którego odwołujesz się, wygenerowany kod klienta dla poprzedniego przykładu będzie podobny do poniższego.
[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
Można określić typy interfejsów kolekcji w ramach przywoływalnych typów kolekcji, ale nie można określić nieprawidłowych typów kolekcji (takich jak te bez Add
metody lub konstruktora publicznego).
Zamknięty rodzaj ogólny jest uważany za najlepszy. (Typy inne niż ogólne są uważane za równoważne zamkniętym rodzajom Object
). Jeśli na przykład typy ogólne List<T>DateTime, ogólne BindingList<T> (otwarte ogólne) i ArrayList są typami kolekcji, generowane są następujące typy.
[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
W przypadku kolekcji list obsługiwane są tylko przypadki z poniższej tabeli.
Typ, do których odwołuje się odwołanie | Interfejs zaimplementowany przez typ odwołania | Przykład | Typ traktowany jako: |
---|---|---|---|
Nieogólne lub zamknięte ogólne (dowolna liczba parametrów) | Nieogólne | MyType : IList lub MyType<T> : IList gdzie T = int |
Zamknięty rodzaj ( Object na przykład IList<object> ) |
Nieogólne lub zamknięte ogólne (dowolna liczba parametrów, które nie muszą być zgodne z typem kolekcji) | Zamknięte ogólne | MyType : IList<string> lub MyType<T> : IList<string> gdzie T =int |
Zamknięte ogólne (na przykład IList<string> ) |
Zamknięte ogólne z dowolną liczbą parametrów | Otwórz ogólny przy użyciu dowolnego z parametrów typu | MyType<T,U,V> : IList<U> where T=, U= int string , V=bool |
Zamknięte ogólne (na przykład IList<string> ) |
Otwórz ogólny z jednym parametrem | Otwórz ogólny przy użyciu parametru typu | MyType<T> : IList<T> , T jest otwarty |
Otwórz ogólny (na przykład IList<T> ) |
Jeśli typ implementuje więcej niż jeden interfejs kolekcji list, obowiązują następujące ograniczenia:
Jeśli typ implementuje typ ogólny IEnumerable<T> (lub jego interfejsy pochodne) wiele razy dla różnych typów, typ nie jest uznawany za prawidłowy typ kolekcji, do których się odwołuje i jest ignorowany. Jest to prawdą, nawet jeśli niektóre implementacje są nieprawidłowe lub używają otwartych typów ogólnych. Na przykład typ implementujący typ Ogólny IEnumerable<T>
int
i Ogólny IEnumerable<T> T nigdy nie będzie używany jako kolekcja przywoływałaint
lub jakikolwiek inny typ, niezależnie od tego, czy typ maAdd
metodę akceptowanąint
, czy metodę akceptującąAdd
parametr typu T, czy oba te typy.Jeśli typ implementuje ogólny interfejs kolekcji, jak IListrównież , typ nigdy nie jest używany jako typ kolekcji, chyba że interfejs kolekcji ogólnej jest zamkniętym rodzajem typu Object.
W przypadku kolekcji słowników obsługiwane są tylko przypadki z poniższej tabeli.
Typ, do których odwołuje się odwołanie | Interfejs zaimplementowany przez typ odwołania | Przykład | Typ traktowany jako |
---|---|---|---|
Nieogólne lub zamknięte ogólne (dowolna liczba parametrów) | IDictionary | MyType : IDictionary lub MyType<T> : IDictionary gdzie T =int |
Zamknięte ogólne IDictionary<object,object> |
Zamknięte ogólne (dowolna liczba parametrów) | IDictionary<TKey,TValue>Zamknięta | MyType<T> : IDictionary<string, bool> gdzie T =int |
Zamknięte ogólne (na przykład IDictionary<string,bool> ) |
Zamknięte ogólne (dowolna liczba parametrów) | Ogólny IDictionary<TKey,TValue>, jeden z klucza lub wartości jest zamknięty, drugi jest otwarty i używa jednego z parametrów typu | MyType<T,U,V> : IDictionary<string,V> where T=int , U=float ,V=bool lub MyType<Z> : IDictionary<Z,bool> gdzie Z =string |
Zamknięte ogólne (na przykład IDictionary<string,bool> ) |
Zamknięte ogólne (dowolna liczba parametrów) | Ogólny IDictionary<TKey,TValue>, zarówno klucz, jak i wartość są otwarte, a każdy z nich używa jednego z parametrów typu | MyType<T,U,V> : IDictionary<V,U> where T=, U=int bool , V=string |
Zamknięte ogólne (na przykład IDictionary<string,bool> ) |
Otwórz ogólny (dwa parametry) | Ogólny IDictionary<TKey,TValue>, otwarty, używa obu parametrów ogólnych typu w kolejności ich wyświetlania | MyType<K,V> : IDictionary<K,V> , K i V otwarte |
Otwórz ogólny (na przykład IDictionary<K,V> ) |
Jeśli typ implementuje zarówno, jak IDictionary i Generic IDictionary<TKey,TValue>, jest brany pod uwagę tylko typ ogólny IDictionary<TKey,TValue> .
Odwoływanie się do częściowych typów ogólnych nie jest obsługiwane.
Duplikaty nie są dozwolone, na przykład nie można dodać zarówno wartości Generic List<T> of Integer
, jak i generic Collection of Integer
to ReferencedCollectionTypes, ponieważ uniemożliwia to określenie, którego z nich należy użyć, gdy lista liczb całkowitych zostanie znaleziona w schemacie. Duplikaty są wykrywane tylko wtedy, gdy istnieje typ schematu, który uwidacznia problem duplikatów. Jeśli na przykład importowany schemat nie zawiera list liczb całkowitych, może mieć zarówno wartość Generic List<T> of Integer
, jak i ogólną kolekcję Integer
w ReferencedCollectionTypesobiekcie , ale żaden z nich nie ma żadnego efektu.
Zaawansowane reguły kolekcji
Serializowanie kolekcji
Poniżej znajduje się lista reguł kolekcji na potrzeby serializacji:
Łączenie typów kolekcji (z kolekcjami kolekcji) jest dozwolone. Tablice postrzępione są traktowane jako kolekcje kolekcji. Tablice wielowymiarowe nie są obsługiwane.
Tablice bajtów XmlNode i tablic obiektów to specjalne typy tablic, które są traktowane jako typy pierwotne, a nie kolekcje. Serializowanie tablicy bajtów powoduje utworzenie pojedynczego elementu XML zawierającego fragment danych zakodowanych w formacie Base64 zamiast oddzielnego elementu dla każdego bajtu. Aby uzyskać więcej informacji na temat sposobu traktowania tablicy XmlNode , zobacz XML i ADO.NET Types in Data Contracts (Typy XML i ADO.NET w kontraktach danych). Oczywiście te specjalne typy mogą uczestniczyć w kolekcjach: tablica tablicy wyników bajtów w wielu elementach XML, z których każdy zawiera fragment danych zakodowanych w formacie Base64.
DataContractAttribute Jeśli atrybut jest stosowany do typu kolekcji, typ jest traktowany jako zwykły typ kontraktu danych, a nie jako kolekcja.
Jeśli typ kolekcji implementuje IXmlSerializable interfejs, obowiązują następujące reguły, biorąc pod uwagę typ
myType:IList<string>, IXmlSerializable
:Jeśli zadeklarowany typ to
IList<string>
, typ jest serializowany jako lista.Jeśli zadeklarowany typ to
myType
, jest serializowany jakoIXmlSerializable
.Jeśli zadeklarowany typ to
IXmlSerializable
, jest serializowany jakoIXmlSerializable
, ale tylko w przypadku dodaniamyType
do listy znanych typów.
Kolekcje są serializowane i deserializowane przy użyciu metod przedstawionych w poniższej tabeli.
Implementowanie typu kolekcji | Metody wywoływane w przypadku serializacji | Metody wywoływane przy deserializacji |
---|---|---|
Ogólny IDictionary<TKey,TValue> | get_Keys , get_Values |
Dodaj ogólny |
IDictionary | get_Keys , get_Values |
Add |
Ogólny IList<T> | Indeksator ogólny IList<T> | Dodaj ogólny |
Ogólny ICollection<T> | Moduł wyliczający | Dodaj ogólny |
IList | IList Indeksatora | Add |
Ogólny IEnumerable<T> | GetEnumerator |
Metoda niestatyczna o nazwie Add , która przyjmuje jeden parametr odpowiedniego typu (typ parametru ogólnego lub jeden z jego typów podstawowych). Taka metoda musi istnieć, aby serializator traktować typ kolekcji jako kolekcję podczas serializacji i deserializacji. |
IEnumerable (i w ten sposób ICollection, który pochodzi z niego) | GetEnumerator |
Metoda niestatyczna o nazwie Add , która przyjmuje jeden parametr typu Object . Taka metoda musi istnieć, aby serializator traktować typ kolekcji jako kolekcję podczas serializacji i deserializacji. |
W powyższej tabeli wymieniono interfejsy kolekcji w kolejności malejącej pierwszeństwa. Oznacza to na przykład, że jeśli typ implementuje zarówno, jak IList i Generic IEnumerable<T>, kolekcja jest serializowana i deserializowana zgodnie z regułami IList :
Podczas deserializacji wszystkie kolekcje są deserializowane przez najpierw utworzenie wystąpienia typu przez wywołanie konstruktora bez parametrów, który musi być obecny dla serializatora, aby traktować typ kolekcji jako kolekcję podczas serializacji i deserializacji.
Jeśli ten sam interfejs kolekcji ogólnej jest implementowany więcej niż raz (na przykład jeśli typ implementuje zarówno typ Generic of
Integer
, jak i Generic ICollection<T>ICollection<T> of ) i nie znaleziono interfejsu Stringwyższego pierwszeństwa, kolekcja nie jest traktowana jako prawidłowa kolekcja.Typy kolekcji mogą mieć SerializableAttribute zastosowany atrybut i mogą implementować ISerializable interfejs. Oba te elementy są ignorowane. Jeśli jednak typ nie spełnia w pełni wymagań dotyczących typu kolekcji (na przykład
Add
brakuje metody), typ nie jest uznawany za typ kolekcji, a tym samym SerializableAttribute atrybut i ISerializable interfejs są używane do określenia, czy typ może być serializowany.Zastosowanie atrybutu CollectionDataContractAttribute do kolekcji w celu dostosowania go spowoduje usunięcie poprzedniego SerializableAttribute mechanizmu rezerwowego. Zamiast tego, jeśli niestandardowa kolekcja nie spełnia wymagań dotyczących typu kolekcji, InvalidDataContractException zgłaszany jest wyjątek. Ciąg wyjątku często zawiera informacje, które wyjaśniają, dlaczego dany typ nie jest uznawany za prawidłową kolekcję (bez
Add
metody, bez konstruktora bez parametrów itd.), dlatego często przydaje się zastosowanie atrybutu CollectionDataContractAttribute do celów debugowania.
Nazewnictwo kolekcji
Poniżej znajduje się lista reguł nazewnictwa kolekcji:
Domyślna przestrzeń nazw dla wszystkich kontraktów danych zbierania słowników, a także kontraktów danych zbierania list zawierających typy pierwotne, jest
http://schemas.microsoft.com/2003/10/Serialization/Arrays
chyba że zostanie zastąpiona przy użyciu przestrzeni nazw. Typy mapowane na wbudowane typy XSD, a takżechar
typy ,Timespan
iGuid
są uważane za typy pierwotne w tym celu.Domyślna przestrzeń nazw dla typów kolekcji zawierających typy inne niż pierwotne, chyba że jest zastępowana przy użyciu przestrzeni nazw, jest taka sama jak przestrzeń nazw kontraktu danych typu zawartego w kolekcji.
Domyślna nazwa kontraktów danych zbierania list, chyba że została zastąpiona przy użyciu nazwy, jest ciągiem "ArrayOf" połączonym z nazwą kontraktu danych typu zawartego w kolekcji. Na przykład nazwa kontraktu danych dla ogólnej listy liczb całkowitych to "ArrayOfint". Należy pamiętać, że nazwa kontraktu
Object
danych to "anyType", więc nazwa kontraktu danych list innych niż ogólne, takich jak ArrayList "ArrayOfanyType".
Domyślna nazwa kontraktów danych zbierania słownika, chyba że zostanie zastąpiona przy użyciu metody Name
, jest ciągiem "ArrayOfKeyValueOf" połączonym z nazwą kontraktu danych typu klucza, a następnie nazwą kontraktu danych typu wartości. Na przykład nazwa kontraktu danych dla ogólnego słownika ciągów i liczby całkowitej to "ArrayOfKeyValueOfstringint". Ponadto jeśli klucz lub typy wartości nie są typami pierwotnymi, skrót przestrzeni nazw przestrzeni nazw przestrzeni nazw klucza i typów wartości jest dołączany do nazwy. Aby uzyskać więcej informacji na temat skrótów przestrzeni nazw, zobacz Nazwy kontraktów danych.
Każdy kontrakt danych zbierania słowników ma kontrakt danych towarzyszący, który reprezentuje jeden wpis w słowniku. Jego nazwa jest taka sama jak w przypadku kontraktu danych słownika, z wyjątkiem prefiksu "ArrayOf", a jego przestrzeń nazw jest taka sama jak w przypadku kontraktu danych słownika. Na przykład dla kontraktu danych słownika "ArrayOfKeyValueOfstringint" kontrakt danych "KeyValueofstringint" reprezentuje jeden wpis w słowniku. Nazwę tego kontraktu danych można dostosować przy użyciu właściwości , zgodnie z opisem ItemName
w następnej sekcji.
Ogólne reguły nazewnictwa typów, zgodnie z opisem w nazwach kontraktów danych, w pełni mają zastosowanie do typów kolekcji. Oznacza to, że można użyć nawiasów klamrowych w polu Nazwa, aby wskazać parametry typu ogólnego. Jednak liczby w nawiasach klamrowych odnoszą się do parametrów ogólnych, a nie typów zawartych w kolekcji.
Dostosowywanie kolekcji
Następujące zastosowania atrybutu CollectionDataContractAttribute są zabronione i powodują wyjątek InvalidDataContractException :
Zastosowanie atrybutu DataContractAttribute do typu, do którego CollectionDataContractAttribute zastosowano atrybut, lub do jednego z jego typów pochodnych.
Zastosowanie atrybutu CollectionDataContractAttribute do typu, który implementuje IXmlSerializable interfejs.
Stosowanie atrybutu CollectionDataContractAttribute do typu innego niż kolekcja.
Próba ustawienia KeyName lub ValueName przy użyciu atrybutu zastosowanego CollectionDataContractAttribute do typu innego niż słownik.
Reguły polimorfizmu
Jak wspomniano wcześniej, dostosowywanie kolekcji przy użyciu atrybutu CollectionDataContractAttribute może zakłócać wymianę kolekcji. Dwa dostosowane typy kolekcji mogą być traktowane jako równoważne tylko wtedy, gdy ich nazwa, przestrzeń nazw, nazwa elementu, a także nazwy kluczy i wartości (jeśli są to kolekcje słowników).
Ze względu na dostosowania można przypadkowo użyć jednego kontraktu danych zbierania, w którym oczekiwano innego. Należy tego unikać. Zobacz następujące typy.
[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
W takim przypadku można przypisać wystąpienie Marks1
do testMarks
klasy . Nie należy jednak używać, Marks2
ponieważ jego kontrakt danych nie jest uznawany za równoważny kontraktowi IList<int>
danych. Nazwa kontraktu danych to "Marks2", a nie "ArrayOfint", a powtarzana nazwa elementu to "<mark>", a nie "<int>".
Reguły w poniższej tabeli dotyczą przypisania polimorficznego kolekcji.
Zadeklarowany typ | Przypisywanie kolekcji, która nie jest niestandardowa | Przypisywanie dostosowanej kolekcji |
---|---|---|
Objekt | Nazwa kontraktu jest serializowana. | Nazwa kontraktu jest serializowana. Dostosowywanie jest używane. |
Interfejs kolekcji | Nazwa kontraktu nie jest serializowana. | Nazwa kontraktu nie jest serializowana. Dostosowywanie nie jest używane.* |
Kolekcja nieskonfigurowana | Nazwa kontraktu nie jest serializowana. | Nazwa kontraktu jest serializowana. Dostosowywanie jest używane.** |
Niestandardowa kolekcja | Nazwa kontraktu jest serializowana. Dostosowywanie nie jest używane.** | Nazwa kontraktu jest serializowana. Jest używane dostosowanie przypisanego typu.** |
*W przypadku NetDataContractSerializer klasy dostosowywanie jest używane w tym przypadku. Klasa NetDataContractSerializer serializuje również rzeczywistą nazwę typu w tym przypadku, więc deserializacja działa zgodnie z oczekiwaniami.
**Te przypadki powodują nieprawidłowe wystąpienia schematu i dlatego należy unikać.
W przypadkach, w których nazwa kontraktu jest serializowana, przypisany typ kolekcji powinien znajdować się na liście znanych typów. Odwrotnie jest również prawdą: w przypadkach, w których nazwa nie jest serializowana, dodanie typu do znanej listy typów nie jest wymagane.
Tablicę typu pochodnego można przypisać do tablicy typu podstawowego. W tym przypadku nazwa kontraktu dla typu pochodnego jest serializowana dla każdego powtarzającego się elementu. Jeśli na przykład typ Book
pochodzi z typu LibraryItem
, można przypisać tablicę Book
do tablicy .LibraryItem
Nie dotyczy to innych typów kolekcji. Na przykład nie można przypisać elementu Generic List of Book
do elementu Generic List of LibraryItem
. Można jednak przypisać wystąpienie Generic List of LibraryItem
zawierające Book
wystąpienia. Zarówno w tablicy, jak i przypadku bez tablicy Book
, powinny znajdować się na liście znanych typów.
Kolekcje i zachowywanie odwołań do obiektów
Gdy serializator działa w trybie, w którym zachowuje odwołania do obiektów, zachowanie odwołania do obiektów ma również zastosowanie do kolekcji. W szczególności tożsamość obiektu jest zachowywana zarówno dla całych kolekcji, jak i poszczególnych elementów zawartych w kolekcjach. W przypadku słowników tożsamość obiektu jest zachowywana zarówno dla obiektów par klucz/wartość, jak i pojedynczy klucz i obiekty wartości.