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>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 i Namespace .

  • 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 i Namespace , 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=intstring, 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ła int lub jakikolwiek inny typ, niezależnie od tego, czy typ ma Add 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=intbool, 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 jako IXmlSerializable.

    • Jeśli zadeklarowany typ to IXmlSerializable, jest serializowany jako IXmlSerializable, ale tylko w przypadku dodania myType 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 ofInteger, 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że chartypy , Timespani Guid 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 :

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 testMarksklasy . 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.

Zobacz też