Share via


Typy kolekcí v kontraktech dat

Kolekce je seznam položek určitého typu. V rozhraní .NET Framework lze takové seznamy reprezentovat pomocí polí nebo různých jiných typů (obecný seznam, obecný BindingList<T>, StringCollectionnebo ArrayList). Kolekce může například obsahovat seznam adres pro daného zákazníka. Tyto kolekce se nazývají kolekce seznamů bez ohledu na jejich skutečný typ.

Existuje speciální forma kolekce, která představuje přidružení mezi jednou položkou ("klíč") a jinou ("hodnota"). V rozhraní .NET Framework jsou reprezentovány typy, jako Hashtable jsou a obecný slovník. Například kolekce přidružení může mapovat město ("klíč") na jeho populaci ("hodnota"). Tyto kolekce se nazývají slovníkové kolekce bez ohledu na jejich skutečný typ.

Kolekce dostávají speciální zacházení v modelu kontraktu dat.

Typy, které implementují IEnumerable rozhraní, včetně polí a obecných kolekcí, se rozpoznávají jako kolekce. Z těchto typů, které implementují IDictionary nebo obecná IDictionary<TKey,TValue> rozhraní, jsou slovníkové kolekce; všechny ostatní jsou kolekce seznamů.

Další požadavky na typy kolekcí, jako je zavolání Add metody a konstruktor bez parametrů, jsou podrobně popsány v následujících částech. Tím se zajistí, že typy kolekcí lze serializovat i deserializovat. To znamená, že některé kolekce nejsou přímo podporovány, například Generic ReadOnlyCollection<T> (protože nemá žádný konstruktor bez parametrů). Informace o obejití těchto omezení najdete v části Používání typů rozhraní kolekce a kolekcí jen pro čtení dále v tomto tématu.

Typy obsažené v kolekcích musí být datové typy kontraktů nebo jinak serializovatelné. Další informace naleznete v tématu Typy podporované serializátorem kontraktu dat.

Další informace o tom, co je a co není považováno za platnou kolekci a o tom, jak jsou kolekce serializovány, naleznete informace o serializaci kolekcí v části "Upřesnit pravidla kolekce" tohoto tématu.

Zaměnitelné kolekce

Všechny kolekce seznamů stejného typu se považují za stejné kontrakty dat (pokud nejsou přizpůsobeny pomocí atributu CollectionDataContractAttribute , jak je popsáno dále v tomto tématu). Například následující datové kontrakty jsou ekvivalentní.

[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 dat mají za následek jazyk XML podobný následujícímu kódu.

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

Zaměnitelnost kolekce umožňuje například použít typ kolekce optimalizovaný pro výkon na serveru a typ kolekce navržený tak, aby byl vázán na součásti uživatelského rozhraní v klientovi.

Podobně jako u kolekcí seznamů jsou všechny slovníkové kolekce se stejnými typy klíč a hodnota považovány za stejné datové kontrakty (pokud je atribut přizpůsobený CollectionDataContractAttribute ).

Záleží pouze na typu datového kontraktu, pokud jde o ekvivalenci shromažďování, nikoli o typy .NET. To znamená, že kolekce Typu1 je považována za ekvivalentní kolekci Type2, pokud Typ1 a Type2 mají ekvivalentní datové kontrakty.

Ne generické kolekce jsou považovány za stejné datové kontrakty jako obecné kolekce typu Object. (Například kontrakty dat pro ArrayList obecné List<T> typy Object jsou stejné.)

Použití typů rozhraní kolekce a kolekcí jen pro čtení

Typy rozhraní kolekce (IEnumerable, obecné IDictionaryIDictionary<TKey,TValue>nebo rozhraní odvozené z těchto rozhraní) se také považují za kontrakty dat shromažďování, které jsou ekvivalentní kontraktům dat kolekce pro skutečné typy kolekcí. Proto je možné deklarovat typ serializovaný jako typ rozhraní kolekce a výsledky jsou stejné jako v případě, že byl použit skutečný typ kolekce. Například následující datové kontrakty jsou ekvivalentní.

[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

Při serializaci, když deklarovaný typ je rozhraní, skutečný typ instance použitý může být libovolný typ, který implementuje toto rozhraní. Omezení popsaná dříve (s konstruktorem bez parametrů a metodou Add ) se nevztahují. Můžete například nastavit adresy v Customer2 na instanci Generic ReadOnlyCollection<T> of Address, i když nelze přímo deklarovat datový člen typu Generic ReadOnlyCollection<T>.

Při deserializaci, když deklarovaný typ je rozhraní, serializace modul zvolí typ, který implementuje deklarované rozhraní a typ je vytvořena instance. Mechanismus známých typů (popsaný v známých typech kontraktů dat) zde nemá žádný vliv. Volba typu je integrovaná do WCF.

Přizpůsobení typů kolekcí

Typy kolekcí můžete přizpůsobit pomocí atributu CollectionDataContractAttribute , který má několik použití.

Upozorňujeme, že přizpůsobení typů kolekcí ohrožuje zaměnitelnost kolekcí, takže obecně doporučujeme vyhnout se použití tohoto atributu, kdykoli je to možné. Další informace o tomto problému najdete v části Rozšířená pravidla kolekce dále v tomto tématu.

Pojmenování kontraktů dat kolekce

Pravidla pro pojmenování typů kolekcí jsou podobná pravidlům pro pojmenování běžných typů kontraktů dat, jak je popsáno v názvech kontraktů dat, i když existují některé důležité rozdíly:

  • Atribut CollectionDataContractAttribute se používá k přizpůsobení názvu místo atributu DataContractAttribute . Atribut CollectionDataContractAttributeName také vlastnosti.Namespace

  • CollectionDataContractAttribute Pokud se atribut nepoužije, výchozí název a obor názvů pro typy kolekcí závisí na názvech a oborech názvů typů obsažených v kolekci. Nejsou ovlivněny názvem a oborem názvů samotného typu kolekce. Příklad najdete v následujících typech.

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

Název datového kontraktu obou typů je ArrayOfstring, nikoli CustomerList1 nebo StringList1. To znamená, že serializace některého z těchto typů na kořenové úrovni poskytuje XML podobný následujícímu kódu.

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

Toto pravidlo pojmenování bylo zvoleno, aby se zajistilo, že jakýkoli jiný než přizpůsobený typ, který představuje seznam řetězců, má stejný kontrakt dat a reprezentaci XML. Díky tomu je možné zaměnitelnost kolekcí. V tomto příkladu jsou customerList1 a StringList1 zcela zaměnitelné.

Při použití atributu CollectionDataContractAttribute se však kolekce stane přizpůsobeným kontraktem dat kolekce, i když nejsou u atributu nastaveny žádné vlastnosti. Název a obor názvů kontraktu dat kolekce pak závisí na samotném typu kolekce. Příklad najdete v následujícím typu.

[CollectionDataContract]
public class CustomerList2 : Collection<string> {}
<CollectionDataContract()>
Public Class CustomerList2
    Inherits Collection(Of String)
End Class

Při serializaci je výsledný kód XML podobný následujícímu.

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

Všimněte si, že tato funkce už není ekvivalentní reprezentaci XML nespravovaných typů.

  • K dalšímu přizpůsobení pojmenování můžete použít vlastnosti Name a Namespace vlastnosti. Podívejte se na následující třídu.

    [CollectionDataContract(Name="cust_list")]
    public class CustomerList3 : Collection<string> {}
    
    <CollectionDataContract(Name:="cust_list")>
    Public Class CustomerList3
        Inherits Collection(Of String)
    End Class
    

Výsledný kód XML je podobný následujícímu.

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

Další informace najdete v části Rozšířená pravidla kolekce dále v tomto tématu.

Přizpůsobení názvu opakujícího se elementu v kolekcích seznamů

Kolekce seznamů obsahují opakující se položky. Za normálních okolností je každá opakující se položka reprezentována jako prvek pojmenovaný podle názvu datového kontraktu typu obsaženého v kolekci.

CustomerList V příkladech kolekce obsahovaly řetězce. Název kontraktu dat pro primitivní typ řetězce je "string", takže opakující se element byl "<string>".

Nicméně pomocí ItemName vlastnosti atributu CollectionDataContractAttribute lze tento opakující se název elementu přizpůsobit. Příklad najdete v následujícím typu.

[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string>  {}
<CollectionDataContract(ItemName:="customer")>
Public Class CustomerList4
    Inherits Collection(Of String)
End Class

Výsledný kód XML je podobný následujícímu.

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

Obor názvů opakujícího se elementu je vždy stejný jako obor názvů kontraktu dat kolekce, který lze přizpůsobit pomocí Namespace vlastnosti, jak je popsáno výše.

Přizpůsobení kolekcí slovníků

Kolekce slovníků jsou v podstatě seznamy položek, kde každá položka má klíč následovaný hodnotou. Stejně jako u běžných seznamů můžete změnit název elementu, který odpovídá opakujícímu se elementu ItemName pomocí vlastnosti.

Kromě toho můžete změnit názvy prvků, které představují klíč a hodnotu pomocí KeyName vlastností ValueName . Obory názvů pro tyto prvky jsou stejné jako obor názvů kontraktu dat kolekce.

Příklad najdete v následujícím typu.

[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

Při serializaci je výsledný kód XML podobný následujícímu.

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

Další informace o kolekcích slovníků najdete v části Rozšířená pravidla kolekce dále v tomto tématu.

Kolekce a známé typy

Při použití polymorfně místo jiných kolekcí nebo rozhraní kolekcí nemusíte přidávat typy kolekcí do známých typů. Pokud například deklarujete datový člen typu IEnumerable a použijete ho ArrayListk odeslání instance , nemusíte přidávat ArrayList známé typy.

Pokud používáte kolekce polymorfně místo typů, které nejsou kolekcemi, je nutné je přidat do známých typů. Pokud například deklarujete datový člen typu Object a použijete ho ArrayListk odeslání instance , přidejte ArrayList do známých typů.

To neumožňuje serializovat žádnou ekvivalentní kolekci polymorfně. Když například přidáte ArrayList do seznamu známých typů v předchozím příkladu, nedovolíte přiřadit Array of Object třídu, i když má ekvivalentní kontrakt dat. To se neliší od běžného chování známých typů při serializaci pro jiné typy než kolekce, ale je zvlášť důležité pochopit v případě kolekcí, protože je velmi běžné, aby kolekce byly ekvivalentní.

Během serializace lze v libovolném rozsahu pro daný kontrakt dat znát pouze jeden typ a ekvivalentní kolekce mají stejné datové kontrakty. To znamená, že v předchozím příkladu nemůžete přidat oba ArrayList typy a Array of Object známé typy ve stejném oboru. Opět je to ekvivalentem chování známých typů pro jiné typy než kolekce, ale je zvláště důležité pochopit pro kolekce.

Známé typy mohou být také vyžadovány pro obsah kolekcí. Pokud například ArrayList skutečně obsahuje instance Type1 a Type2, oba tyto typy by měly být přidány do známých typů.

Následující příklad ukazuje správně vytvořený objektový graf pomocí kolekcí a známých typů. Příklad je poněkud neposoudný, protože ve skutečné aplikaci byste obvykle nedefinovali následující datové členy jako Object, a proto nemají žádné známé problémy s typem nebo polymorfismem.

[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

Při deserializaci, pokud deklarovaný typ je typ kolekce, deklarovaný typ je vytvořena instance bez ohledu na typ, který byl skutečně odeslán. Pokud je deklarovaný typ rozhraní kolekce, deserializátor vybere typ, který se má vytvořit, bez ohledu na známé typy.

Při deserializaci platí, že pokud deklarovaný typ není typem kolekce, ale odesílá se typ kolekce, vybere se odpovídající typ kolekce ze seznamu známých typů. Do seznamu známých typů pro deserializaci je možné přidat typy rozhraní kolekce. V tomto případě modul deserializace znovu vybere typ, který se má vytvořit.

Kolekce a NetDataContractSerializer – třída

NetDataContractSerializer Pokud se třída používá, nepřizpůsobené typy kolekcí (bez atributuCollectionDataContractAttribute), které nejsou polemi, ztratí svůj zvláštní význam.

Neupravované typy kolekcí označené atributem SerializableAttribute mohou být serializovány NetDataContractSerializer podle atributu SerializableAttribute nebo ISerializable pravidel rozhraní.

Přizpůsobené typy kolekcí, rozhraní kolekcí a pole jsou stále považovány za kolekce, i když NetDataContractSerializer je třída používána.

Kolekce a schéma

Všechny ekvivalentní kolekce mají stejnou reprezentaci ve schématu XSD (XML Schema Definition Language). Z tohoto důvodu obvykle nezískáte stejný typ kolekce ve vygenerovaném klientském kódu jako ten na serveru. Server může například použít datový kontrakt s obecným List<T> datovým členem integer, ale ve vygenerovaném klientském kódu se stejný datový člen může stát polem celých čísel.

Kolekce slovníků jsou označeny poznámkami schématu specifické pro WCF, které označují, že jsou slovníky; jinak jsou nerozlišitelné od jednoduchých seznamů, které obsahují položky s klíčem a hodnotou. Přesný popis způsobu znázornění kolekcí ve schématu kontraktů dat naleznete v tématu Referenční informace ke schématu kontraktu dat.

Ve výchozím nastavení se typy negenerují pro neupravované kolekce v importovaném kódu. Datové členy typů kolekcí seznamů se importují jako pole a datové členy typů kolekcí slovníku se importují jako obecný slovník.

U přizpůsobených kolekcí se však generují samostatné typy označené atributem CollectionDataContractAttribute . (Přizpůsobený typ kolekce ve schématu je ten, který nepoužívá výchozí obor názvů, název, opakující se název elementu nebo názvy elementů klíč/hodnota.) Tyto typy jsou prázdné typy, které jsou odvozeny od obecné List<T> pro typy seznamů a Obecný slovník pro typy slovníku.

Na serveru můžete mít například následující 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

Při exportu a importu schématu zpět je vygenerovaný klientský kód podobný následujícímu (pole se zobrazí místo vlastností pro snadné čtení).

[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

Ve vygenerovaném kódu můžete použít různé typy, než jsou výchozí. Například můžete chtít použít obecné BindingList<T> místo běžných polí pro datové členy, aby bylo snazší je svázat s komponentami uživatelského rozhraní.

Pokud chcete zvolit typy kolekcí, které chcete vygenerovat, předejte seznam typů kolekcí, které chcete použít, do ReferencedCollectionTypes vlastnosti objektu ImportOptions při importu schématu. Tyto typy se nazývají odkazované typy kolekcí.

Při odkazování na obecné typy musí být buď plně otevřené obecné typy, nebo plně uzavřené obecné typy.

Poznámka:

Při použití nástroje Svcutil.exe lze tento odkaz provést pomocí přepínače příkazového řádku /collectionType (krátký formulář: /ct). Mějte na paměti, že musíte také zadat sestavení pro odkazované typy kolekcí pomocí přepínače /reference (krátký formulář: /r). Pokud je typ obecný, musí za ním následovat zpětná uvozovka a počet obecných parametrů. Zadní uvozovka (') není zaměňována s jednoduchým znakem uvozovek ('). Více odkazovaných typů kolekcí můžete zadat pomocí přepínače /collectionType více než jednou.

Chcete-li například způsobit import všech seznamů jako obecný List<T>.

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

Při importu jakékoli kolekce se tento seznam odkazovaných typů kolekcí zkontroluje a použije se nejvhodnější kolekce, pokud se najde, buď jako typ datového členu (pro jiné než přizpůsobené kolekce), nebo jako základní typ odvozený od (pro přizpůsobené kolekce). Slovníky se shodují pouze se slovníky, zatímco seznamy se shodují se seznamy se seznamy.

Pokud například přidáte obecný BindingList<T> a Hashtable do seznamu odkazovaných typů, vygenerovaný kód klienta pro předchozí příklad je podobný následujícímu.

[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

Jako součást odkazovaných typů kolekcí můžete zadat typy rozhraní kolekce, ale nemůžete zadat neplatné typy kolekcí (například typy bez Add metody nebo veřejného konstruktoru).

Uzavřený obecný se považuje za nejlepší shodu. (Negenerické typy jsou považovány za ekvivalentní uzavřeným obecným typům Object). Pokud jsou například typy Generic List<T> of , Generic BindingList<T> (open generic) a ArrayList jsou odkazované typy kolekce, vygeneruje DateTimese následující.

[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

U kolekcí seznamů se podporují pouze případy v následující tabulce.

Odkazovaný typ Rozhraní implementované odkazovaným typem Příklad Typ se považuje za:
Obecné nebo uzavřené obecné typy (libovolný počet parametrů) Jiné než obecné MyType : IList

nebo

MyType<T> : IList

where T= int
Uzavřený obecný ( Object například IList<object>)
Ne generické nebo uzavřené obecné (libovolný počet parametrů, které nemusí nutně odpovídat typu kolekce) Uzavřený obecný MyType : IList<string>

nebo

MyType<T> : IList<string> where T=int
Uzavřený obecný (například IList<string>)
Uzavřený obecný s libovolným počtem parametrů Otevření obecného pomocí libovolného z parametrů typu MyType<T,U,V> : IList<U>

where T=int, U=string, V=bool
Uzavřený obecný (například IList<string>)
Otevření obecného s jedním parametrem Otevření obecného pomocí parametru typu MyType<T> : IList<T>, T je otevřená Otevřít obecný (například IList<T>)

Pokud typ implementuje více než jedno rozhraní kolekce seznamů, platí následující omezení:

  • Pokud typ implementuje obecné IEnumerable<T> (nebo jeho odvozená rozhraní) vícekrát pro různé typy, typ se nepovažuje za platný odkazovaný typ kolekce a bude ignorován. To platí i v případě, že některé implementace jsou neplatné nebo používají otevřené obecné typy. Například typ, který implementuje Generic IEnumerable<T> of a Generic IEnumerable<T> of int T by nikdy nebyl použit jako odkazovaná kolekce int nebo jakýkoli jiný typ, bez ohledu na to, zda typ má metodu Add přijetí int nebo metoduAdd, která přijímá parametr typu T, nebo obojí.

  • Pokud typ implementuje obecné rozhraní kolekce a také IList, typ se nikdy nepoužívá jako odkazovaný typ kolekce, pokud obecné rozhraní kolekce není uzavřený obecný typ .Object

U slovníkových kolekcí se podporují pouze případy v následující tabulce.

Odkazovaný typ Rozhraní implementované odkazovaným typem Příklad Typ se považuje za
Obecné nebo uzavřené obecné typy (libovolný počet parametrů) IDictionary MyType : IDictionary

nebo

MyType<T> : IDictionary where T=int
Uzavřený obecný IDictionary<object,object>
Uzavřený obecný (libovolný počet parametrů) IDictionary<TKey,TValue>Uzavřené MyType<T> : IDictionary<string, bool> where T=int Uzavřený obecný (například IDictionary<string,bool>)
Uzavřený obecný (libovolný počet parametrů) Obecný IDictionary<TKey,TValue>, jeden z klíčů nebo hodnot je uzavřen, druhý je otevřen a používá jeden z parametrů typu MyType<T,U,V> : IDictionary<string,V> where T=int, U=float,V=bool

nebo

MyType<Z> : IDictionary<Z,bool> where Z=string
Uzavřený obecný (například IDictionary<string,bool>)
Uzavřený obecný (libovolný počet parametrů) Obecné IDictionary<TKey,TValue>, klíč i hodnota jsou otevřené a každý používá jeden z parametrů typu MyType<T,U,V> : IDictionary<V,U> where T=int, U=bool, V=string Uzavřený obecný (například IDictionary<string,bool>)
Otevření obecného typu (dva parametry) Obecné IDictionary<TKey,TValue>, otevřené, používá oba obecné parametry typu v pořadí, v jakém se zobrazují. MyType<K,V> : IDictionary<K,V>, K i V jsou otevřené Otevřít obecný (například IDictionary<K,V>)

Pokud typ implementuje jak IDictionary obecný IDictionary<TKey,TValue>, považuje se za obecný IDictionary<TKey,TValue> .

Odkazování na částečné obecné typy není podporováno.

Duplicity nejsou povoleny, například nelze přidat generické List<T> a Integer obecné kolekce Integer do ReferencedCollectionTypes, protože to znemožňuje určit, který z nich se má použít, když je v schématu nalezen seznam celých čísel. Duplicitní položky jsou zjištěny pouze v případě, že ve schématu existuje typ, který zveřejňuje problém s duplicitami. Pokud například importované schéma neobsahuje seznamy celých čísel, může mít List<T>Integer obecné i obecné kolekce Integer v objektu ReferencedCollectionTypes, ale ani jedno nemá žádný vliv.

Rozšířená pravidla shromažďování

Serializace kolekcí

Následuje seznam pravidel kolekce pro serializaci:

  • Kombinování typů kolekcí (s kolekcemi kolekcí) je povoleno. Jagged arrays are treated as collections of collections. Multidimenzionální pole nejsou podporována.

  • Pole bajtů a polí XmlNode jsou speciální typy polí, které jsou považovány za primitivy, nikoli jako kolekce. Serializace pole bajtů má za následek jeden element XML, který obsahuje blok dat s kódováním Base64, místo samostatného prvku pro každý bajt. Další informace o tom, jak se zpracovává pole XmlNode , naleznete v tématu XML a ADO.NET Typy v kontraktech dat. Samozřejmě, tyto speciální typy se mohou účastnit kolekcí: pole pole bajtů vede k více prvkům XML, přičemž každý obsahuje blok dat s kódováním Base64.

  • DataContractAttribute Pokud je atribut použit na typ kolekce, typ je považován za běžný datový kontrakt, nikoli jako kolekce.

  • Pokud typ kolekce implementuje IXmlSerializable rozhraní, platí následující pravidla vzhledem k typu myType:IList<string>, IXmlSerializable:

    • Pokud je IList<string>deklarovaný typ , typ je serializován jako seznam.

    • Pokud je myTypedeklarovaný typ , je serializován jako IXmlSerializable.

    • Pokud je IXmlSerializabledeklarovaný typ , je serializován jako IXmlSerializable, ale pouze pokud přidáte myType do seznamu známých typů.

  • Kolekce jsou serializovány a deserializovány pomocí metod zobrazených v následující tabulce.

Implementuje se typ kolekce. Metody volané při serializaci Metody volané při deserializaci
Obecné IDictionary<TKey,TValue> get_Keys, get_Values Obecný doplněk
IDictionary get_Keys, get_Values Add
Obecné IList<T> Obecný IList<T> indexer Obecný doplněk
Obecné ICollection<T> Čítač výčtu Obecný doplněk
IList IList Indexer Add
Obecné IEnumerable<T> GetEnumerator Nestatická metoda, Add která přebírá jeden parametr odpovídajícího typu (typ obecného parametru nebo některého z jeho základních typů). Taková metoda musí existovat, aby serializátor zacházet s typem kolekce jako kolekcí během serializace i deserializace.
IEnumerable (a proto ICollection, který je odvozen od něj) GetEnumerator Nestatická metoda, Add která přebírá jeden parametr typu Object. Taková metoda musí existovat, aby serializátor zacházet s typem kolekce jako kolekcí během serializace i deserializace.

Předchozí tabulka uvádí rozhraní kolekce v sestupném pořadí podle priority. To například znamená, že pokud typ implementuje jak IList a obecný IEnumerable<T>, kolekce je serializována a deserializována podle IList pravidel:

  • Při deserializaci jsou všechny kolekce deserializovány tak, že nejprve vytvoří instanci typu voláním konstruktoru bez parametrů, který musí být přítomn pro serializátor zacházet s typem kolekce jako kolekcí během serializace i deserializace.

  • Pokud je stejné obecné rozhraní kolekce implementováno více než jednou (například pokud typ implementuje obecný ICollection<T> i Integer obecný ICollection<T> ) Stringa není nalezeno žádné rozhraní s vyšší prioritou, kolekce není považována za platnou kolekci.

  • Typy kolekcí mohou mít SerializableAttribute atribut použitý na ně a mohou implementovat ISerializable rozhraní. Obě tyto hodnoty jsou ignorovány. Pokud však typ plně nesplňuje požadavky na typ kolekce (například Add metoda chybí), typ není považován za typ kolekce, a proto SerializableAttribute se atribut a ISerializable rozhraní používají k určení, zda lze typ serializovat.

  • Použití atributu CollectionDataContractAttribute na kolekci k přizpůsobení odebere SerializableAttribute předchozí záložní mechanismus. Pokud přizpůsobená kolekce nesplňuje požadavky na typ kolekce, vyvolá InvalidDataContractException se výjimka. Řetězec výjimky často obsahuje informace, které vysvětlují, proč daný typ není považován za platnou kolekci (žádná Add metoda, žádný konstruktor bez parametrů atd.), takže je často užitečné použít CollectionDataContractAttribute atribut pro účely ladění.

Pojmenování kolekce

Následuje seznam pravidel pojmenování kolekce:

  • Výchozí obor názvů pro všechny kontrakty dat kolekce slovníku a také pro kontrakty dat kolekce seznamů, které obsahují primitivní typy, není http://schemas.microsoft.com/2003/10/Serialization/Arrays přepsáno pomocí oboru názvů. Typy mapované na předdefinované typy XSD a chartaké , Timespana Guid typy jsou pro tento účel považovány za primitivní.

  • Výchozí obor názvů pro typy kolekcí, které obsahují jiné než primitivní typy, pokud není přepsán pomocí oboru názvů, je stejný jako obor názvů datového kontraktu typu obsaženého v kolekci.

  • Výchozí název kontraktů dat kolekce seznamů, pokud není přepsán pomocí name, je řetězec "ArrayOf" v kombinaci s názvem datového kontraktu typu obsaženého v kolekci. Například název kontraktu dat pro obecný seznam celých čísel je "ArrayOfint". Mějte na paměti, že název datového kontraktu Object je anyType, takže název kontraktu dat jiných než obecných seznamů, jako ArrayList je ArrayOfanyType.

Výchozí název kontraktů dat kolekce slovníku, pokud není přepsán pomocí Name, je řetězec "ArrayOfKeyValueOf" v kombinaci s názvem datového kontraktu typu klíče následovaný názvem datového kontraktu typu hodnoty. Například název kontraktu dat pro obecný slovník řetězce a integer je "ArrayOfKeyValueOfstringint". Navíc, pokud klíč nebo typy hodnot nejsou primitivní typy, je k názvu připojena hodnota hash oboru názvů datového kontraktu typu klíč a hodnota. Další informace o hashách oboru názvů naleznete v tématu Názvy kontraktů dat.

Každý kontrakt dat kolekce slovníku má doprovodný kontrakt dat, který představuje jednu položku ve slovníku. Jeho název je stejný jako pro kontrakt dat slovníku, s výjimkou předpony "ArrayOf" a jeho obor názvů je stejný jako pro kontrakt dat slovníku. Například u kontraktu dat slovníku ArrayOfKeyValueOfstringint představuje kontrakt dat KeyValueofstringint jednu položku ve slovníku. Název tohoto kontraktu dat můžete přizpůsobit pomocí ItemName vlastnosti, jak je popsáno v další části.

Obecná pravidla pojmenování typů, jak je popsáno v názvech kontraktů dat, se plně vztahují na typy kolekcí. To znamená, že můžete použít složené závorky v názvu k označení parametrů obecného typu. Čísla v závorkách ale odkazují na obecné parametry a ne typy obsažené v kolekci.

Přizpůsobení kolekce

Následující použití atributu CollectionDataContractAttribute jsou zakázáno a výsledkem je InvalidDataContractException výjimka:

Pravidla polymorfismu

Jak jsme už zmínili, přizpůsobení kolekcí pomocí atributu CollectionDataContractAttribute může kolidovat s zaměnitelností kolekce. Dva přizpůsobené typy kolekcí lze považovat za ekvivalentní pouze v případě, že se jejich název, obor názvů, název položky a názvy klíčů a hodnot (pokud se jedná o slovníkové kolekce) odpovídají.

Z důvodu přizpůsobení je možné neúmyslně použít jeden kontrakt dat kolekce, kde se očekává jiný. Mělo by se tomu vyhnout. Podívejte se na následující 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

V tomto případě je možné přiřadit testMarksinstanci Marks1 . Nemělo by se však používat, Marks2 protože její datový kontrakt se nepovažuje za ekvivalent datového kontraktu IList<int> . Název kontraktu dat je "Marks2" a ne "ArrayOfint" a název opakujícího se prvku je "<mark>" a nikoli "<int>".

Pravidla v následující tabulce platí pro polymorfní přiřazení kolekcí.

Deklarovaný typ Přiřazení nepřizpůsobené kolekce Přiřazení přizpůsobené kolekce
Object Název kontraktu je serializován. Název kontraktu je serializován.

Používá se vlastní nastavení.
Rozhraní kolekce Název kontraktu není serializován. Název kontraktu není serializován.

Vlastní nastavení se nepoužívá.*
Nesezpůsobené kolekce Název kontraktu není serializován. Název kontraktu je serializován.

Používá se vlastní nastavení.**
Přizpůsobená kolekce Název kontraktu je serializován. Vlastní nastavení se nepoužívá.** Název kontraktu je serializován.

Použije se vlastní nastavení přiřazeného typu.**

*U NetDataContractSerializer třídy se v tomto případě použije vlastní nastavení. Třída NetDataContractSerializer také serializuje skutečný název typu v tomto případě, takže deserializace funguje podle očekávání.

**Tyto případy mají za následek neplatné instance schématu, a proto by se měly vyhnout.

V případech, kdy je název kontraktu serializován, by přiřazený typ kolekce měl být v seznamu známých typů. Opak je také pravdivý: v případech, kdy název není serializován, přidání typu do seznamu známých typů není vyžadováno.

Pole odvozeného typu lze přiřadit k poli základního typu. V tomto případě je název kontraktu odvozeného typu serializován pro každý opakující se prvek. Pokud je například typ Book odvozen od typu LibraryItem, můžete přiřadit pole Book pole .LibraryItem To neplatí pro jiné typy kolekcí. Nemůžete například přiřadit k objektu Generic List of BookGeneric List of LibraryItem. Můžete ale přiřadit Generic List of LibraryItem instanci, která obsahuje Book instance. V případě pole i jiného typu Book by měl být v seznamu známých typů.

Zachování odkazů na objekty a kolekce

Když serializátor funkce v režimu, kde zachovává odkazy na objekty, zachování odkazů na objekt platí také pro kolekce. Konkrétně je identita objektu zachována pro celé kolekce i jednotlivé položky obsažené v kolekcích. Pro slovníky je identita objektu zachována jak pro objekty páru klíč/hodnota, tak pro jednotlivé objekty klíče a hodnoty.

Viz také