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 CollectionDataContractAttribute má
Name
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
aNamespace
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á kolekceint
nebo jakýkoli jiný typ, bez ohledu na to, zda typ má metoduAdd
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
myType
deklarovaný typ , je serializován jakoIXmlSerializable
.Pokud je
IXmlSerializable
deklarovaný typ , je serializován jakoIXmlSerializable
, ale pouze pokud přidátemyType
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 achar
také ,Timespan
aGuid
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:
Použití atributu DataContractAttribute na typ, na který CollectionDataContractAttribute byl atribut použit, nebo na jeden z jeho odvozených typů.
Použití atributu CollectionDataContractAttribute na typ, který implementuje IXmlSerializable rozhraní.
Použití atributu CollectionDataContractAttribute u jiného typu kolekce.
Při pokusu o nastavení KeyName nebo ValueName u atributu použitého CollectionDataContractAttribute u jiného typu než slovníku.
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 testMarks
instanci 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 Book
Generic 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.