Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Une collection est une liste d’éléments d’un certain type. Dans .NET Framework, ces listes peuvent être représentées à l’aide de tableaux ou d’un large éventail d’autres types (Liste générique, Générique BindingList<T>, StringCollectionou ArrayList). Par exemple, une collection peut contenir une liste d’adresses pour un client donné. Ces collections sont appelées collections de listes, quel que soit leur type réel.
Une forme spéciale de collection existe qui représente une association entre un élément (la « clé ») et une autre (la « valeur »). Dans le .NET Framework, ceux-ci sont représentés par des types tels que Hashtable et le dictionnaire générique. Par exemple, une collection associative peut associer une ville (“clé”) à sa population (“valeur”). Ces collections sont appelées collections de dictionnaires, quel que soit leur type réel.
Les collections reçoivent un traitement spécial dans le modèle de contrat de données.
Les types qui implémentent l’interface IEnumerable , y compris les tableaux et les collections génériques, sont reconnus comme des collections. Parmi ceux-ci, les types qui implémentent les interfaces IDictionary ou génériques IDictionary<TKey,TValue> sont des collections de dictionnaire ; tous les autres sont des collections de liste.
Des exigences supplémentaires sur les types de collection, telles qu’une méthode appelée Add
et un constructeur sans paramètre, sont décrites en détail dans les sections suivantes. Cela garantit que les types de collection peuvent être sérialisés et désérialisés. Cela signifie que certaines collections ne sont pas directement prises en charge, telles que le générique ReadOnlyCollection<T> (car il n’a pas de constructeur sans paramètre). Toutefois, pour plus d’informations sur le contournement de ces restrictions, consultez la section « Using Collection Interface Types and Read-Only Collections » plus loin dans cette rubrique.
Les types contenus dans les collections doivent être des types de contrat de données ou être sérialisables. Pour plus d’informations, consultez Types pris en charge par le sérialiseur de contrat de données.
Pour plus d’informations sur ce qui est et ce qui n’est pas considéré comme une collection valide, ainsi que sur la façon dont les regroupements sont sérialisés, consultez les informations sur la sérialisation des collections dans la section « Règles avancées de collection » de cette rubrique.
Collections interchangeables
Toutes les collections de listes du même type sont considérées comme ayant le même contrat de données (sauf si elles sont personnalisées à l’aide de l’attribut CollectionDataContractAttribute , comme indiqué plus loin dans cette rubrique). Par exemple, les contrats de données suivants sont équivalents.
[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
Les deux contrats de données entraînent des données XML similaires au code suivant.
<PurchaseOrder>
<customerName>...</customerName>
<items>
<Item>...</Item>
<Item>...</Item>
<Item>...</Item>
...
</items>
<comments>
<string>...</string>
<string>...</string>
<string>...</string>
...
</comments>
</PurchaseOrder>
L’échange de collection vous permet d’utiliser, par exemple, un type de collection optimisé pour les performances sur le serveur et un type de collection conçu pour être lié aux composants de l’interface utilisateur sur le client.
À l’instar des collections de listes, toutes les collections de dictionnaire qui ont les mêmes types clé et valeur sont considérées comme ayant le même contrat de données (sauf si elles sont personnalisées par l’attribut CollectionDataContractAttribute ).
Seul le type de contrat de données est important en ce qui concerne l’équivalence de collection, pas les types .NET. Autrement dit, une collection de Type1 est considérée comme équivalente à une collection de Type2 si Type1 et Type2 ont des contrats de données équivalents.
Les collections non génériques sont considérées comme ayant le même contrat de données que les collections génériques de type Object
. (Par exemple, les contrats de données pour ArrayList et Générique List<T> de Object
sont les mêmes.)
Utilisation des types d'interfaces de collection et des collections en lecture seule
Les types d’interface de collecte (IEnumerable, IDictionarygénériques IDictionary<TKey,TValue>ou interfaces dérivées de ces interfaces) sont également considérés comme ayant des contrats de données de collecte, équivalents aux contrats de données de collecte pour les types de collecte réels. Par conséquent, il est possible de déclarer le type sérialisé en tant que type d’interface de collection et les résultats sont identiques si un type de collection réel avait été utilisé. Par exemple, les contrats de données suivants sont équivalents.
[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
Lors de la sérialisation, lorsque le type déclaré est une interface, le type d’instance réel utilisé peut être n’importe quel type qui implémente cette interface. Les restrictions décrites précédemment (avoir un constructeur sans paramètre et une Add
méthode) ne s’appliquent pas. Par exemple, vous pouvez définir des adresses dans Customer2 sur une instance de Generic ReadOnlyCollection<T> of Address, même si vous ne pouvez pas déclarer directement un membre de données de type Générique ReadOnlyCollection<T>.
Lors de la désérialisation, lorsque le type déclaré est une interface, le moteur de sérialisation choisit un type qui implémente l’interface déclarée et le type est instancié. Le mécanisme de types connus (décrit dans Les types connus de contrat de données) n’a aucun effet ici ; le choix du type est intégré à WCF.
Personnalisation des types de collection
Vous pouvez personnaliser les types de collection à l’aide de l’attribut CollectionDataContractAttribute , qui a plusieurs utilisations.
Notez que la personnalisation des types de collection compromet l’échange de collection. Il est donc généralement recommandé d’éviter d’appliquer cet attribut dans la mesure du possible. Pour plus d’informations sur ce problème, consultez la section « Règles de collection avancées » plus loin dans cette rubrique.
Attribution de noms aux contrats de données de collection
Les règles de nomination des types de collection sont similaires à celles pour nommer les types de contrats de données réguliers, comme décrit dans Les noms de contrats de données, bien que certaines différences importantes existent.
L’attribut CollectionDataContractAttribute est utilisé pour personnaliser le nom, au lieu de l’attribut DataContractAttribute . L’attribut CollectionDataContractAttribute possède également les propriétés
Name
etNamespace
.Lorsque l’attribut CollectionDataContractAttribute n’est pas appliqué, le nom et l’espace de noms par défaut pour les types de collection dépendent des noms et espaces de noms des types contenus dans la collection. Ils ne sont pas affectés par le nom du type de collection ni par son espace de noms. Pour obtenir un exemple, consultez les types suivants.
public CustomerList1 : Collection<string> {} public StringList1 : Collection<string> {}
Le nom du contrat de données des deux types est « ArrayOfstring » et non « CustomerList1 » ou « StringList1 ». Cela signifie que la sérialisation d’un de ces types au niveau racine génère du code XML similaire au code suivant.
<ArrayOfstring>
<string>...</string>
<string>...</string>
<string>...</string>
...
</ArrayOfstring>
Cette règle de nommage a été choisie pour s’assurer que tout type non personnalisé qui représente une liste de chaînes a le même contrat de données et la représentation XML. Cela rend possible l'interchangeabilité des collections. Dans cet exemple, CustomerList1 et StringList1 sont complètement interchangeables.
Toutefois, lorsque l’attribut CollectionDataContractAttribute est appliqué, la collection devient un contrat de données de collecte personnalisé, même si aucune propriété n’est définie sur l’attribut. Le nom et l’espace de noms du contrat de données de collecte dépendent ensuite du type de collection lui-même. Pour obtenir un exemple, consultez le type suivant.
[CollectionDataContract]
public class CustomerList2 : Collection<string> {}
<CollectionDataContract()>
Public Class CustomerList2
Inherits Collection(Of String)
End Class
En cas de sérialisation, le code XML résultant est similaire à ce qui suit.
<CustomerList2>
<string>...</string>
<string>...</string>
<string>...</string>
...
</CustomerList2>
Notez que cela n’est plus équivalent à la représentation XML des types non personnalisés.
Vous pouvez utiliser les propriétés
Name
etNamespace
pour personnaliser davantage le nommage. Consultez la classe suivante.[CollectionDataContract(Name="cust_list")] public class CustomerList3 : Collection<string> {}
<CollectionDataContract(Name:="cust_list")> Public Class CustomerList3 Inherits Collection(Of String) End Class
Le code XML obtenu est similaire à ce qui suit.
<cust_list>
<string>...</string>
<string>...</string>
<string>...</string>
...
</cust_list>
Pour plus d’informations, consultez la section « Règles de collecte avancées » plus loin dans cette rubrique.
Personnalisation du nom d’élément répétitif dans les collections de listes
Les collections de listes contiennent des entrées répétées. Normalement, chaque entrée répétée est représentée en tant qu’élément nommé en fonction du nom du contrat de données du type contenu dans la collection.
Dans les CustomerList
exemples, les collections contenaient des chaînes. Le nom du contrat de données pour le type primitif de chaîne est « string », de sorte que l’élément répétitif était «< string> ».
Toutefois, à l’aide de la ItemName propriété sur l’attribut CollectionDataContractAttribute , ce nom d’élément répétitif peut être personnalisé. Pour obtenir un exemple, consultez le type suivant.
[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string> {}
<CollectionDataContract(ItemName:="customer")>
Public Class CustomerList4
Inherits Collection(Of String)
End Class
Le code XML obtenu est similaire à ce qui suit.
<CustomerList4>
<customer>...</customer>
<customer>...</customer>
<customer>...</customer>
...
</CustomerList4>
L’espace de noms de l’élément répétitif est toujours identique à l’espace de noms du contrat de données de collecte, qui peut être personnalisé à l’aide de la Namespace
propriété, comme décrit précédemment.
Personnalisation des collections de dictionnaires
Les collections de dictionnaires sont essentiellement des listes d’entrées, où chaque entrée a une clé suivie d’une valeur. Tout comme avec les listes régulières, vous pouvez modifier le nom de l’élément qui correspond à l’élément répétitif à l’aide de la ItemName propriété.
En outre, vous pouvez modifier les noms des éléments qui représentent la clé et la valeur à l'aide des propriétés KeyName et ValueName. Les espaces de noms de ces éléments sont identiques à l’espace de noms du contrat de données de collecte.
Pour obtenir un exemple, consultez le type suivant.
[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
En cas de sérialisation, le code XML résultant est similaire à ce qui suit.
<CountriesOrRegionsWithCapitals>
<entry>
<countryorregion>USA</countryorregion>
<capital>Washington</capital>
</entry>
<entry>
<countryorregion>France</countryorregion>
<capital>Paris</capital>
</entry>
...
</CountriesOrRegionsWithCapitals>
Pour plus d’informations sur les collections de dictionnaires, consultez la section « Règles de collection avancées » plus loin dans cette rubrique.
Collections et types connus
Vous n’avez pas besoin d’ajouter des types de collection aux types connus lorsqu’ils sont utilisés polymorphiquement à la place d’autres collections ou interfaces de collection. Par exemple, si vous déclarez un membre de données de type IEnumerable et que vous l’utilisez pour envoyer une instance de ArrayList, vous n’avez pas besoin d’ajouter ArrayList à des types connus.
Lorsque vous utilisez des collections polymorphes à la place de types non-collection, elles doivent être ajoutées aux types connus. Par exemple, si vous déclarez un membre de données de type Object
et que vous l'utilisez pour envoyer une instance de ArrayList, ajoutez ArrayList aux types connus.
Cela ne vous permet pas de sérialiser une collection équivalente polymorphiquement. Par exemple, lorsque vous ajoutez ArrayList à la liste des types connus dans l’exemple précédent, cela ne vous permet pas d’affecter la Array of Object
classe, même si elle a un contrat de données équivalent. Cela n'est pas différent du comportement des types connus ordinaires concernant la sérialisation des types non-collection, mais il est particulièrement important de comprendre cela dans le cas des collections, car il est très fréquent que des collections soient équivalentes.
Lors de la sérialisation, un seul type peut être connu dans n’importe quelle étendue donnée pour un contrat de données donné, et les collections équivalentes ont tous les mêmes contrats de données. Cela signifie que, dans l’exemple précédent, vous ne pouvez pas ajouter à la fois ArrayList et Array of Object
aux types connus dans la même étendue. Encore une fois, cela est équivalent au comportement des types connus pour les types qui ne sont pas des collections, mais cela est particulièrement important à comprendre pour les collections.
Les types connus peuvent également être requis pour le contenu des collections. Par exemple, si un ArrayList contient réellement des instances de Type1
et de Type2
, ces deux types doivent être ajoutés aux types connus.
L’exemple suivant montre un graphique d’objet construit correctement à l’aide de collections et de types connus. L’exemple est quelque peu contrif, car dans une application réelle, vous ne définissez normalement pas les membres de données suivants comme Object
, et n’avez donc pas de problèmes connus de type/polymorphisme.
[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
Lors de la désérialisation, si le type déclaré est un type de collection, le type déclaré est instancié indépendamment du type qui a été réellement envoyé. Si le type déclaré est une interface de collection, le désérialiseur sélectionne un type à instancier sans tenir compte des types connus.
En outre, lors de la désérialisation, si le type déclaré n’est pas un type de collection, mais qu’un type de collection est envoyé, un type de collection correspondant est sélectionné dans la liste des types connus. Il est possible d’ajouter des types d’interface de collection à la liste des types connus lors de la désérialisation. Dans ce cas, le moteur de désérialisation choisit de nouveau un type à instancier.
Collections et classe NetDataContractSerializer
Lorsque la NetDataContractSerializer classe est en cours d’utilisation, les types de collection non personnalisés (sans l’attribut CollectionDataContractAttribute ) qui ne sont pas des tableaux perdent leur signification spéciale.
Les types de collection non personnalisés marqués avec l’attribut SerializableAttribute peuvent toujours être sérialisés par la NetDataContractSerializer classe en fonction de l’attribut SerializableAttribute ou des ISerializable règles d’interface.
Les types de collection personnalisés, les interfaces de collection et les tableaux sont toujours traités comme des collections, même lorsque la NetDataContractSerializer classe est en cours d’utilisation.
Collections et schéma
Toutes les collections équivalentes ont la même représentation dans le schéma XSD (XML Schema Definition Language). En raison de cela, vous n’obtenez normalement pas le même type de collection dans le code client généré que celui sur le serveur. Par exemple, le serveur peut utiliser un contrat de données avec un List<T> membre générique de données Integer, mais dans le code client généré, le même membre de données peut devenir un tableau d’entiers.
Les collections de dictionnaires sont marquées d'une annotation de schéma spécifique à WCF qui indiquent qu'il s’agit de dictionnaires ; sinon, elles sont indistinguables de listes simples qui contiennent des entrées avec une clé et une valeur. Pour obtenir une description exacte de la façon dont les collections sont représentées dans le schéma de contrat de données, consultez Informations de référence sur le schéma du contrat de données.
Par défaut, les types ne sont pas générés pour les collections non personnalisées dans le code importé. Les membres de données des types de collections liste sont importés en tant que tableaux, alors que les membres de données des types de collections dictionnaire sont importés en tant que dictionnaire générique.
Toutefois, pour les collections personnalisées, des types distincts sont générés, marqués avec l’attribut CollectionDataContractAttribute . (Un type de collection personnalisé dans le schéma est un type qui n’utilise pas l’espace de noms par défaut, le nom par défaut, le nom d’élément répétitif par défaut, ou les noms d’éléments clé/valeur par défaut.) Ces types sont des types vides qui dérivent de Generic List<T> pour les types de liste et de le Dictionnaire Générique pour les types de dictionnaire.
Par exemple, vous pouvez avoir les types suivants sur le serveur.
[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
Lorsque le schéma est exporté et importé à nouveau, le code client généré est similaire à ce qui suit (les champs sont affichés au lieu de propriétés pour faciliter la lecture).
[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
Vous souhaiterez peut-être utiliser différents types dans le code généré que ceux par défaut. Par exemple, vous pouvez utiliser Generic BindingList<T> au lieu de tableaux standard pour vos membres de données afin de faciliter leur liaison aux composants de l’interface utilisateur.
Pour choisir les types de collection à générer, transmettez une liste de types de collection que vous souhaitez utiliser dans la ReferencedCollectionTypes propriété de l’objet lors de l’importation ImportOptions du schéma. Ces types sont appelés types de collection référencés.
Lorsque des types génériques sont référencés, ils doivent être des génériques entièrement ouverts ou des génériques entièrement fermés.
Remarque
Lorsque vous utilisez l’outil Svcutil.exe, cette référence peut être effectuée à l’aide du commutateur de ligne de commande /collectionType (forme abrégée : /ct). N’oubliez pas que vous devez également spécifier l’assembly pour les types de collection référencés à l’aide du commutateur /reference (forme abrégée : /r). Si le type est générique, il doit être suivi d’un guillemet précédent et du nombre de paramètres génériques. Le guillemet précédent (') ne doit pas être confondu avec le caractère guillemet unique ('). Vous pouvez spécifier plusieurs types de collection référencés à l’aide du commutateur /collectionType plusieurs fois.
Par exemple, pour que toutes les listes soient importées en tant que génériques List<T>.
svcutil.exe MyService.wsdl MyServiceSchema.xsd /r:C:\full_path_to_system_dll\System.dll /ct:System.Collections.Generic.List`1
Lors de l’importation d’une collection, cette liste de types de collection référencés est analysée et la collection la plus adaptée est utilisée si une collection est trouvée, soit en tant que type de membre de données (pour les collections non personnalisées) soit en tant que type de base à dériver (pour les collections personnalisées). Les dictionnaires sont mis en correspondance uniquement par rapport aux dictionnaires, tandis que les listes sont mises en correspondance avec les listes.
Par exemple, si vous ajoutez le générique BindingList<T> et Hashtable à la liste des types référencés, le code client généré pour l’exemple précédent est similaire à ce qui suit.
[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
Vous pouvez spécifier des types d’interface de collection dans le cadre de vos types de collection référencés, mais vous ne pouvez pas spécifier de types de collection non valides (comme ceux Add
sans méthode ni constructeur public).
Un générique fermé est considéré comme la solution la mieux adaptée. (Les types non génériques sont considérés comme équivalents aux génériques fermés de Object
). Par exemple, si les types Generic List<T> of DateTime, Generic BindingList<T> (open generic) et ArrayList sont les types de collection référencés, les éléments suivants sont générés.
[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
Pour les regroupements de listes, seuls les cas du tableau suivant sont pris en charge.
Type référencé | Interface implémentée par type référencé | Exemple : | Type traité comme : |
---|---|---|---|
Générique non-standard ou générique fermé (n'importe quel nombre de paramètres) | Non générique | MyType : IList ou MyType<T> : IList où T= int |
Générique fermé de Object (par exemple, IList<object> ) |
Non-générique ou générique fermé (tout nombre de paramètres ne correspondant pas nécessairement au type de collection) | Générique fermé | MyType : IList<string> ou MyType<T> : IList<string> où T=int |
Générique fermé (par exemple, IList<string> ) |
Générique fermé avec un nombre quelconque de paramètres | Ouvrir un générique en utilisant l’un des paramètres du type | MyType<T,U,V> : IList<U> where T= int , U=string , V=bool |
Générique fermé (par exemple, IList<string> ) |
Ouvrir générique avec un paramètre | Générique ouvert utilisant le paramètre du type |
MyType<T> : IList<T> , T est ouvert |
Générique ouvert (par exemple, IList<T> ) |
Si un type implémente plusieurs interfaces de collection de listes, les restrictions suivantes s’appliquent :
Si le type implémente plusieurs fois le générique IEnumerable<T> (ou ses interfaces dérivées) pour différents types, il n’est pas considéré comme un type de collection référencé valide et est ignoré. Cela est vrai même si certaines implémentations ne sont pas valides ou utilisent des génériques ouverts. Par exemple, un type qui implémente Generic IEnumerable<T> de
int
et Generic IEnumerable<T> de T ne serait jamais utilisé comme une collection référencée deint
ou de tout autre type, que le type ait une méthodeAdd
acceptantint
ou une méthodeAdd
acceptant un paramètre de type T, ou les deux.Si le type implémente une interface de collection générique ainsi que IList, le type n’est jamais utilisé comme type de collection référencé, sauf si l’interface de collection générique est un générique fermé de type Object.
Pour les collections de dictionnaires, seuls les cas du tableau suivant sont pris en charge.
Type référencé | Interface implémentée par type référencé | Exemple : | Type traité comme |
---|---|---|---|
Générique non-standard ou générique fermé (n'importe quel nombre de paramètres) | IDictionary | MyType : IDictionary ou MyType<T> : IDictionary où T=int |
Générique fermé IDictionary<object,object> |
Générique fermé (n’importe quel nombre de paramètres) | IDictionary<TKey,TValue>, fermé |
MyType<T> : IDictionary<string, bool> où T=int |
Générique fermé (par exemple, IDictionary<string,bool> ) |
Générique fermé (n’importe quel nombre de paramètres) | Générique IDictionary<TKey,TValue>, l’une des clés ou de la valeur est fermée, l’autre est ouverte et utilise l’un des paramètres du type |
MyType<T,U,V> : IDictionary<string,V> where T=int , U=float ,V=bool ou MyType<Z> : IDictionary<Z,bool> où Z=string |
Générique fermé (par exemple, IDictionary<string,bool> ) |
Générique fermé (n’importe quel nombre de paramètres) | Générique IDictionary<TKey,TValue>, clé et valeur sont ouverts et chacun utilise l’un des paramètres du type |
MyType<T,U,V> : IDictionary<V,U> where T=int , U=bool , V=string |
Générique fermé (par exemple, IDictionary<string,bool> ) |
Ouvrir un type générique avec deux paramètres | Générique IDictionary<TKey,TValue>, ouvert, utilise les deux paramètres génériques du type dans l’ordre dans lequel ils apparaissent |
MyType<K,V> : IDictionary<K,V> , K et V sont tous les deux ouverts |
Générique ouvert (par exemple, IDictionary<K,V> ) |
Si le type implémente à la fois IDictionary et Générique IDictionary<TKey,TValue>, seul le générique IDictionary<TKey,TValue> est pris en compte.
Le référencement de types génériques partiels n’est pas pris en charge.
Les doublons ne sont pas autorisés. Par exemple, vous ne pouvez pas ajouter le générique List<T> de Integer
et la collection générique de Integer
à ReferencedCollectionTypes, car cela rend impossible de déterminer lequel utiliser lorsqu'une liste d'entiers est trouvée dans le schéma. Les doublons sont détectés uniquement s’il existe un type dans le schéma qui expose le problème des doublons. Par exemple, si le schéma en cours d'importation ne contient pas de listes d'entiers, il est possible d'avoir à la fois le Générique List<T> de Integer
et la collection générique de Integer
dans le ReferencedCollectionTypes, mais aucun n'a d'effet.
Règles de collecte avancées
Sérialisation des collections
Voici une liste de règles de collecte pour la sérialisation :
L'association de types de collections (avoir des collections de collections) est autorisée. Les tableaux en escalier sont traités comme des collections de collections. Les tableaux multidimensionnels ne sont pas pris en charge.
Les tableaux d'octets et les tableaux de XmlNode sont des types de tableaux spéciaux qui sont traités comme des primitives, et non comme des collections. La sérialisation d’un tableau d’octets entraîne un seul élément XML qui contient un bloc de données encodées en Base64, au lieu d’un élément distinct pour chaque octet. Pour plus d'informations sur le traitement d'un tableau de XmlNode, consultez XML et les types ADO.NET dans les contrats de données. Bien sûr, ces types spéciaux peuvent eux-mêmes participer à des collections : un tableau d’octets entraîne plusieurs éléments XML, chacun contenant un bloc de données codées en Base64.
Si l’attribut DataContractAttribute est appliqué à un type de collection, le type est traité comme un type de contrat de données standard, et non comme une collection.
Si un type de collection implémente l’interface IXmlSerializable , les règles suivantes s’appliquent, en fonction d’un type
myType:IList<string>, IXmlSerializable
:Si le type déclaré est
IList<string>
, le type est sérialisé en tant que liste.Si le type déclaré est
myType
, il est sérialisé en tant queIXmlSerializable
.Si le type déclaré est
IXmlSerializable
, il est sérialisé en tant queIXmlSerializable
, mais uniquement si vous ajoutezmyType
à la liste des types connus.
Les collections sont sérialisées et désérialisées à l’aide des méthodes indiquées dans le tableau suivant.
Le type de collection implémente | Méthode(s) appelée(s) lors de la sérialisation | Méthodes appelées lors de la désérialisation |
---|---|---|
Générique IDictionary<TKey,TValue> |
get_Keys , get_Values |
Ajout générique |
IDictionary |
get_Keys , get_Values |
Add |
Générique IList<T> | Indexeur générique IList<T> | Ajout générique |
Générique ICollection<T> | Énumérateur | Ajout générique |
IList | IList Indexeur | Add |
Générique IEnumerable<T> | GetEnumerator |
Méthode non statique appelée Add qui accepte un paramètre du type approprié (le type du paramètre générique ou l’un de ses types de base). Une telle méthode doit exister pour que le sérialiseur traite un type de collection comme une collection pendant la sérialisation et la désérialisation. |
IEnumerable (et donc ICollection, qui dérive de celui-ci) | GetEnumerator |
Méthode non statique appelée Add qui accepte un paramètre de type Object . Une telle méthode doit exister pour que le sérialiseur traite un type de collection comme une collection pendant la sérialisation et la désérialisation. |
Le tableau précédent répertorie les interfaces de collection dans l’ordre décroissant de priorité. Cela signifie, par exemple, que si un type implémente à la fois IList et générique IEnumerable<T>, la collection est sérialisée et désérialisée en fonction des IList règles :
Lors de la désérialisation, toutes les collections sont désérialisées en créant d’abord une instance du type en appelant le constructeur sans paramètre, qui doit être présent pour que le sérialiseur traite un type de collection comme une collection pendant la sérialisation et la désérialisation.
Si la même interface de collection générique est implémentée plusieurs fois (par exemple, si un type implémente à la fois générique ICollection<T> et
Integer
générique ICollection<T> de String) et qu’aucune interface de priorité supérieure n’est trouvée, la collection n’est pas traitée comme une collection valide.Les types de collection peuvent avoir l’attribut SerializableAttribute appliqué à eux et peuvent implémenter l’interface ISerializable . Ces deux éléments sont ignorés. Toutefois, si le type ne répond pas entièrement aux exigences de type de collection (par exemple, la
Add
méthode est manquante), le type n’est pas considéré comme un type de collection, et par conséquent l’attribut SerializableAttribute et l’interface ISerializable sont utilisés pour déterminer si le type peut être sérialisé.L’application de l’attribut CollectionDataContractAttribute à une collection pour la personnaliser supprime le SerializableAttribute mécanisme de secours précédent. À la place, si une collection personnalisée ne satisfait pas les spécifications de type de collection, une exception InvalidDataContractException est levée. La chaîne d’exception contient souvent des informations expliquant pourquoi un type donné n’est pas considéré comme une collection valide (aucune méthode, aucun
Add
constructeur sans paramètre, et ainsi de suite), il est donc souvent utile d’appliquer l’attribut CollectionDataContractAttribute à des fins de débogage.
Nommage de collection
Voici une liste de règles d’affectation de noms de collection :
L’espace de noms par défaut pour tous les contrats de données de collection dictionnaire, ainsi que pour les contrats de données de collection liste qui contiennent des types primitifs, est
http://schemas.microsoft.com/2003/10/Serialization/Arrays
à moins qu’il ait été remplacé à l’aide de Namespace. Les types qui mappent aux types XSD intégrés, ainsi que les typeschar
,Timespan
, etGuid
, sont considérés comme des primitives à cet effet.L'espace de noms par défaut des types de collections contenant des types non primitifs, à moins qu'il soit remplacé à l'aide de Namespace, est le même que celui des noms de contrat de données du type inclus dans la collection.
Le nom par défaut pour les contrats de données de collection de liste, à moins qu'il ne soit remplacé à l'aide de Name, est la chaîne "ArrayOf" associée au nom du contrat de données du type inclus dans la collection. Par exemple, le nom du contrat de données pour une liste générique d’entiers est « ArrayOfint ». N’oubliez pas que le nom du contrat de données de
Object
est « anyType », alors le nom du contrat de données des listes non génériques comme ArrayList est « ArrayOfanyType ».
Le nom par défaut des contrats de données des collections de dictionnaires, sauf substitution avec Name
, est la chaîne « ArrayOfKeyValueOf » combinée avec le nom du contrat de données du type de clé suivi du nom du contrat de données du type de valeur. Par exemple, le nom du contrat de données pour un dictionnaire générique de chaîne et d’entier est « ArrayOfKeyValueOfstringint ». En outre, si les types de clé ou de valeur ne sont pas des types primitifs, un hachage d'espace de noms des espaces de noms de contrat de données des types de clé et de valeur est ajouté au nom. Pour plus d’informations sur les hachages d’espace de noms, consultez Noms des contrats de données.
Chaque contrat de données de collecte de dictionnaire a un contrat de données complémentaire qui représente une entrée dans le dictionnaire. Son nom est le même que pour le contrat de données de dictionnaire, à l’exception du préfixe « ArrayOf », et son espace de noms est le même que pour le contrat de données de dictionnaire. Par exemple, pour le contrat de données de dictionnaire « ArrayOfKeyValueOfstringint », le contrat de données « KeyValueofstringint » représente une entrée dans le dictionnaire. Vous pouvez personnaliser le nom de ce contrat de données à l’aide de la ItemName
propriété, comme décrit dans la section suivante.
Règles de nommage de type générique, comme décrit dans Les noms de contrat de données, s’appliquent entièrement aux types de collection ; autrement dit, vous pouvez utiliser des accolades dans Name pour indiquer les paramètres de type générique. Toutefois, les nombres dans les accolades font référence aux paramètres génériques et non aux types contenus dans la collection.
Personnalisation de la collection
Les utilisations suivantes de l’attribut CollectionDataContractAttribute sont interdites et entraînent une InvalidDataContractException exception :
Application de l’attribut DataContractAttribute à un type auquel l’attribut CollectionDataContractAttribute a été appliqué ou à l’un de ses types dérivés.
Application de l’attribut CollectionDataContractAttribute à un type qui implémente l’interface IXmlSerializable .
Application de l'attribut CollectionDataContractAttribute à un type qui n'est pas une collection.
Tentative de définition de KeyName ou ValueName sur un attribut CollectionDataContractAttribute appliqué à un type non dictionnaire.
Règles de polymorphisme
Comme mentionné précédemment, la personnalisation des collections à l’aide de l’attribut CollectionDataContractAttribute peut interférer avec l’interchangeabilité des collections. Deux types de collection personnalisés ne peuvent être considérés comme équivalents que si leur nom, leur espace de noms, leur nom d’élément, ainsi que les noms de clé et de valeur (s’il s’agit de collections de dictionnaires) correspondent.
En raison de personnalisations, il est possible d’utiliser par inadvertance un contrat de données de collecte où un autre est attendu. Cela doit être évité. Consultez les types suivants.
[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
Dans ce cas, une instance de Marks1
peut être affectée à testMarks
. Toutefois, Marks2
ne doit pas être utilisé, car son contrat de données n’est pas considéré comme équivalent au contrat de IList<int>
données. Le nom du contrat de données est « Marks2 » et non « ArrayOfint », et le nom de l’élément répétitif est «< mark> » et non «< int> ».
Les règles du tableau suivant s’appliquent à l’affectation polymorphe de collections.
Type déclaré | Assignation d'une collection non personnalisée | Affectation d’une collection personnalisée |
---|---|---|
Objet | Le nom de contrat est sérialisé. | Le nom de contrat est sérialisé. La personnalisation est utilisée. |
Interface de collection | Le nom du contrat n’est pas sérialisé. | Le nom du contrat n’est pas sérialisé. La personnalisation n’est pas utilisée.* |
Collection non personnalisée | Le nom du contrat n’est pas sérialisé. | Le nom de contrat est sérialisé. La personnalisation est utilisée.** |
Collection personnalisée | Le nom de contrat est sérialisé. La personnalisation n’est pas utilisée.** | Le nom de contrat est sérialisé. La personnalisation du type assigné est utilisée.** |
*Avec la classe NetDataContractSerializer, la personnalisation est utilisée dans ce cas. La NetDataContractSerializer classe sérialise également le nom de type réel dans ce cas, de sorte que la désérialisation fonctionne comme prévu.
** De telles situations génèrent des instances de schéma non valides et doivent donc être évitées.
Dans les cas où le nom du contrat est sérialisé, le type de collection affecté doit se trouver dans la liste des types connus. L’inverse est également vrai : dans les cas où le nom n’est pas sérialisé, l’ajout du type à la liste des types connus n’est pas obligatoire.
Un tableau d’un type dérivé peut être affecté à un tableau d’un type de base. Dans ce cas, le nom du contrat pour le type dérivé est sérialisé pour chaque élément répétitif. Par exemple, si un type Book
dérive du type LibraryItem
, vous pouvez affecter un tableau de Book
à un tableau de LibraryItem
. Cela ne s’applique pas aux autres types de collection. Par exemple, vous ne pouvez pas affecter un Generic List of Book
à un Generic List of LibraryItem
. Toutefois, vous pouvez affecter un Generic List of LibraryItem
qui contient des instances de Book
. Dans le cas du tableau et du cas non-matriciel, Book
doit figurer dans la liste des types connus.
Collections et conservation des références d’objet
Lorsqu’un sérialiseur fonctionne dans un mode où il conserve les références d’objet, la conservation des références d’objet s’applique également aux collections. Plus précisément, l’identité d’objet est conservée pour les collections entières et les éléments individuels contenus dans les collections. Pour les dictionnaires, l’identité d’objet est conservée à la fois pour les objets de paire clé/valeur et les objets clé et valeur individuels.