Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Uma coleção é uma lista de itens de um determinado tipo. No .NET Framework, essas listas podem ser representadas usando matrizes ou uma variedade de outros tipos (Lista Genérica, Genérico BindingList<T>, StringCollectionou ArrayList). Por exemplo, uma coleção pode conter uma lista de Endereços para um determinado Cliente. Essas coleções são chamadas de coleções de listas, independentemente de seu tipo real.
Existe uma forma especial de coleção que representa uma associação entre um item (a "chave") e outro (o "valor"). No .NET Framework, eles são representados por tipos como Hashtable e o dicionário genérico. Por exemplo, uma coleção de associações pode mapear uma cidade ("chave") para a sua população ("valor"). Essas coleções são chamadas de coleções de dicionário, independentemente de seu tipo real.
As coleções recebem tratamento especial no modelo de contrato de dados.
Os tipos que implementam a IEnumerable interface, incluindo matrizes e coleções genéricas, são reconhecidos como coleções. Desses, os tipos que implementam as interfaces IDictionary ou IDictionary<TKey,TValue> genéricas são coleções de dicionário; todas as outras são coleções de lista.
Requisitos adicionais sobre tipos de coleção, como ter um método chamado Add e um construtor sem parâmetros, são discutidos detalhadamente nas seções a seguir. Isso garante que os tipos de coleção possam ser serializados e desserializados. Isso significa que algumas coleções não têm suporte direto, como a Genérica ReadOnlyCollection<T> (porque não tem construtor sem parâmetros). No entanto, para obter informações sobre como contornar essas restrições, consulte a seção "Usando tipos de interface de coleção e coleções de Read-Only" mais adiante neste tópico.
Os tipos contidos em coleções devem ser tipos de contrato de dados ou ser serializáveis de outra forma. Para obter mais informações, consulte Tipos compatíveis com o Serializador de Contrato de Dados.
Para obter mais informações sobre o que é e o que não é considerado uma coleção válida, bem como sobre como as coleções são serializadas, consulte as informações sobre serialização de coleções na seção "Regras avançadas de coleção" deste tópico.
Coleções intercambiáveis
Todas as coleções de listas do mesmo tipo são consideradas com o mesmo contrato de dados (a menos que sejam personalizadas usando o CollectionDataContractAttribute atributo, conforme discutido posteriormente neste tópico). Assim, por exemplo, os contratos de dados a seguir são equivalentes.
[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
Ambos os contratos de dados resultam em XML semelhante ao código a seguir.
<PurchaseOrder>
<customerName>...</customerName>
<items>
<Item>...</Item>
<Item>...</Item>
<Item>...</Item>
...
</items>
<comments>
<string>...</string>
<string>...</string>
<string>...</string>
...
</comments>
</PurchaseOrder>
A intercambiabilidade de coleção permite que você use, por exemplo, um tipo de coleção otimizado para desempenho no servidor e um tipo de coleção projetado para ser associado a componentes de interface do usuário no cliente.
Semelhante às coleções de listas, todas as coleções de dicionário que têm os mesmos tipos de chave e valor são consideradas com o mesmo contrato de dados (a menos que sejam personalizadas pelo CollectionDataContractAttribute atributo).
Somente o tipo de contrato de dados importa no que diz respeito à equivalência de coleção, não aos tipos .NET. Ou seja, uma coleção de Type1 é considerada equivalente a uma coleção de Type2 se Type1 e Type2 tiverem contratos de dados equivalentes.
Coleções não genéricas são consideradas com o mesmo contrato de dados que coleções genéricas do tipo Object. (Por exemplo, os contratos de dados para ArrayList e Genérico List<T> de Object são os mesmos.)
Usando tipos de interface de coleção e coleções somente leitura
Os tipos de interface de coleção (IEnumerable, IDictionarygenéricos IDictionary<TKey,TValue>ou interfaces derivadas dessas interfaces) também são considerados como tendo contratos de dados de coleta, equivalentes a contratos de dados de coleta para tipos de coleta reais. Assim, é possível declarar o tipo que está sendo serializado como um tipo de interface de coleção e os resultados são iguais se um tipo de coleção real tivesse sido usado. Por exemplo, os contratos de dados a seguir são equivalentes.
[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
Durante a serialização, quando o tipo declarado é uma interface, o tipo de instância real usado pode ser qualquer tipo que implementa essa interface. As restrições discutidas anteriormente (ter um construtor sem parâmetros e um Add método) não se aplicam. Por exemplo, você pode definir endereços no Customer2 para uma instância do Generic ReadOnlyCollection<T> of Address, mesmo que não seja possível declarar diretamente um membro de dados do tipo Genérico ReadOnlyCollection<T>.
Durante a desserialização, quando o tipo declarado é uma interface, o mecanismo de serialização escolhe um tipo que implementa a interface declarada e o tipo é instanciado. O mecanismo de tipos conhecidos (descrito em Tipos Conhecidos do Contrato de Dados) não tem efeito aqui; a escolha do tipo é integrada ao WCF.
Personalizando tipos de coleção
Você pode personalizar tipos de coleção usando o CollectionDataContractAttribute atributo, que tem vários usos.
Observe que a personalização de tipos de coleção compromete a intercambiabilidade da coleção, portanto, geralmente é recomendável evitar a aplicação desse atributo sempre que possível. Para obter mais informações sobre esse problema, consulte a seção "Regras avançadas de coleção" mais adiante neste tópico.
Nomenclatura do contrato de dados de coleta
As regras para tipos de coleta de nomenclatura são semelhantes às para nomear tipos de contrato de dados regulares, conforme descrito em Nomes de Contrato de Dados, embora existam algumas diferenças importantes:
O CollectionDataContractAttribute atributo é usado para personalizar o nome, em vez do DataContractAttribute atributo. O CollectionDataContractAttribute atributo também tem
NameeNamespacepropriedades.Quando o CollectionDataContractAttribute atributo não é aplicado, o nome padrão e o namespace para tipos de coleção dependem dos nomes e namespaces dos tipos contidos na coleção. Eles não são afetados pelo nome e pelo namespace do próprio tipo de coleção. Para obter um exemplo, consulte os tipos a seguir.
public CustomerList1 : Collection<string> {} public StringList1 : Collection<string> {}
O nome do contrato de dados de ambos os tipos é "ArrayOfstring" e não "CustomerList1" ou "StringList1". Isso significa que serializar qualquer um desses tipos no nível raiz gera XML semelhante ao código a seguir.
<ArrayOfstring>
<string>...</string>
<string>...</string>
<string>...</string>
...
</ArrayOfstring>
Essa regra de nomenclatura foi escolhida para garantir que qualquer tipo não personalizado que represente uma lista de cadeias de caracteres tenha o mesmo contrato de dados e representação XML. Isso possibilita a intercambiabilidade da coleção. Neste exemplo, CustomerList1 e StringList1 são completamente intercambiáveis.
No entanto, quando o CollectionDataContractAttribute atributo é aplicado, a coleção se torna um contrato de dados de coleta personalizado, mesmo que nenhuma propriedade seja definida no atributo. O nome e o namespace do contrato de dados de coleta dependem do próprio tipo de coleção. Para obter um exemplo, consulte o tipo a seguir.
[CollectionDataContract]
public class CustomerList2 : Collection<string> {}
<CollectionDataContract()>
Public Class CustomerList2
Inherits Collection(Of String)
End Class
Quando serializado, o XML resultante é semelhante ao seguinte.
<CustomerList2>
<string>...</string>
<string>...</string>
<string>...</string>
...
</CustomerList2>
Observe que isso não é mais equivalente à representação XML dos tipos não personalizados.
Você pode usar as
Namepropriedades eNamespacepara personalizar ainda mais a nomenclatura. Observe a seguinte classe.[CollectionDataContract(Name="cust_list")] public class CustomerList3 : Collection<string> {}<CollectionDataContract(Name:="cust_list")> Public Class CustomerList3 Inherits Collection(Of String) End Class
O XML resultante é semelhante ao seguinte.
<cust_list>
<string>...</string>
<string>...</string>
<string>...</string>
...
</cust_list>
Para obter mais informações, consulte a seção "Regras avançadas de coleta" mais adiante neste tópico.
Personalizando o nome do elemento recorrente em coleções de listas
As coleções de listas contêm entradas recorrentes. Normalmente, cada entrada recorrente é representada como um elemento nomeado de acordo com o nome do contrato de dados do tipo contido na coleção.
CustomerList Nos exemplos, as coleções continham cadeias de caracteres. O nome do contrato de dados para o tipo primitivo de cadeia de caracteres é "string", portanto, o elemento recorrente era "<string>".
No entanto, usando a ItemName propriedade no CollectionDataContractAttribute atributo, esse nome de elemento recorrente pode ser personalizado. Para obter um exemplo, consulte o tipo a seguir.
[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string> {}
<CollectionDataContract(ItemName:="customer")>
Public Class CustomerList4
Inherits Collection(Of String)
End Class
O XML resultante é semelhante ao seguinte.
<CustomerList4>
<customer>...</customer>
<customer>...</customer>
<customer>...</customer>
...
</CustomerList4>
O namespace do elemento de repetição é sempre o mesmo que o namespace do contrato de dados de coleta, que pode ser personalizado usando a Namespace propriedade, conforme descrito anteriormente.
Personalizando coleções de dicionários
Coleções de dicionário são essencialmente listas de entradas, em que cada entrada tem uma chave seguida por um valor. Assim como acontece com listas regulares, você pode alterar o nome do elemento que corresponde ao elemento recorrente usando a ItemName propriedade.
Além disso, você pode alterar os nomes dos elementos que representam a chave e o valor usando as propriedades KeyName e ValueName. Os namespaces desses elementos são iguais ao namespace do contrato de dados de coleta.
Para obter um exemplo, consulte o tipo a seguir.
[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
Quando serializado, o XML resultante é semelhante ao seguinte.
<CountriesOrRegionsWithCapitals>
<entry>
<countryorregion>USA</countryorregion>
<capital>Washington</capital>
</entry>
<entry>
<countryorregion>France</countryorregion>
<capital>Paris</capital>
</entry>
...
</CountriesOrRegionsWithCapitals>
Para obter mais informações sobre coleções de dicionários, consulte a seção "Regras avançadas de coleção" mais adiante neste tópico.
Coleções e tipos conhecidos
Você não precisa adicionar tipos de coleção a tipos conhecidos quando usados polimorficamente no lugar de outras coleções ou interfaces de coleção. Por exemplo, se você declarar um membro de dados do tipo IEnumerable e usá-lo para enviar uma instância de ArrayList, não será necessário adicionar ArrayList aos tipos conhecidos.
Quando você usa coleções polimorficamente no lugar de tipos não colecionáveis, elas devem ser adicionadas a tipos conhecidos. Por exemplo, se você declarar um membro de dados do tipo Object e usá-lo para enviar uma instância de ArrayList, adicione ArrayList a tipos conhecidos.
Isso não permite serializar qualquer coleção equivalente polimorficamente. Por exemplo, quando você adiciona ArrayList à lista de tipos conhecidos no exemplo anterior, isso não permite atribuir a Array of Object classe, mesmo que ela tenha um contrato de dados equivalente. Isso não é diferente do comportamento de tipos conhecidos regulares na serialização para tipos que não são de coleção, mas é especialmente importante entender no caso de coleções porque é muito comum que as coleções sejam equivalentes.
Durante a serialização, apenas um tipo pode ser conhecido em qualquer escopo determinado para um determinado contrato de dados e coleções equivalentes têm todos os mesmos contratos de dados. Isso significa que, no exemplo anterior, você não pode adicionar ambos ArrayList e Array of Object a tipos conhecidos no mesmo escopo. Mais uma vez, isso é equivale ao comportamento de tipos conhecidos para tipos que não são de coleção, mas é importante principalmente para entender as coleções.
Tipos conhecidos também podem ser necessários para conteúdo de coleções. Por exemplo, se um ArrayList realmente contiver instâncias de Type1 e Type2, ambos os tipos devem ser adicionados a tipos conhecidos.
O exemplo a seguir mostra um grafo de objeto construído corretamente usando coleções e tipos conhecidos. O exemplo é um pouco inventado, pois em um aplicativo real você normalmente não definiria os membros de dados a seguir como Objecte, portanto, não tem problemas conhecidos de tipo/polimorfismo.
[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
Na desserialização, se o tipo declarado for um tipo de coleção, o tipo declarado será instanciado independentemente do tipo que foi realmente enviado. Se o tipo declarado for uma interface de coleção, o desserializador escolherá um tipo a ser instanciado sem considerar tipos conhecidos.
Também na desserialização, se o tipo declarado não for um tipo de coleção, mas um tipo de coleção estiver sendo enviado, um tipo de coleção correspondente será escolhido da lista de tipos conhecidos. É possível adicionar tipos de interface de coleção à lista de tipos conhecidos na desserialização. Nesse caso, o mecanismo de desserialização escolhe novamente um tipo para criar uma instância.
Coleções e a classe NetDataContractSerializer
Quando a NetDataContractSerializer classe está em uso, tipos de coleção não personalizados (sem o CollectionDataContractAttribute atributo) que não são matrizes perdem seu significado especial.
Tipos de coleção não personalizados marcados com o SerializableAttribute atributo ainda podem ser serializados pela NetDataContractSerializer classe de acordo com o SerializableAttribute atributo ou as regras de ISerializable interface.
Tipos de coleção personalizados, interfaces de coleção e matrizes ainda são tratados como coleções, mesmo quando a NetDataContractSerializer classe está em uso.
Coleções e esquema
Todas as coleções equivalentes têm a mesma representação no esquema XSD (linguagem de definição de esquema XML). Por isso, normalmente você não obtém o mesmo tipo de coleção no código do cliente gerado que o do servidor. Por exemplo, o servidor pode usar um contrato de dados com um List<T> genérico de membros de dados Inteiros, mas no código do cliente gerado, o mesmo membro de dados pode se tornar uma matriz de inteiros.
Coleções de dicionário são marcadas com uma anotação de esquema específica do WCF que indica que são dicionários; caso contrário, eles são indistinguíveis de listas simples que contêm entradas com uma chave e um valor. Para obter uma descrição exata de como as coleções são representadas no esquema de contrato de dados, consulte Referência de esquema de contrato de dados.
Por padrão, os tipos não são gerados para coleções não personalizadas no código importado. Os membros de dados dos tipos de coleção de listas são importados como matrizes e os membros de dados dos tipos de coleção de dicionários são importados como Dicionário Genérico.
No entanto, para coleções personalizadas, tipos separados são gerados, marcados com o CollectionDataContractAttribute atributo. (Um tipo de coleção personalizado no esquema é aquele que não usa o namespace padrão, o nome, o nome do elemento repetido ou os nomes de elemento chave/valor.) Esses tipos são tipos vazios que derivam de Genérico List<T> para tipos de lista e Dicionário Genérico para tipos de dicionário.
Por exemplo, você pode ter os seguintes tipos no servidor.
[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
Quando o esquema é exportado e importado novamente, o código do cliente gerado é semelhante ao seguinte (os campos são mostrados em vez de propriedades para facilitar a leitura).
[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
Talvez você queira usar tipos diferentes no código gerado do que os padrão. Por exemplo, talvez você queira usar genérico BindingList<T> em vez de matrizes regulares para seus membros de dados para facilitar a associação deles aos componentes da interface do usuário.
Para escolher tipos de coleção a serem gerados, passe uma lista de tipos de coleção que você deseja usar na ReferencedCollectionTypes propriedade do ImportOptions objeto ao importar o esquema. Esses tipos são chamados de tipos de coleção referenciados.
Quando tipos genéricos estão sendo referenciados, eles devem ser genéricos totalmente abertos ou genéricos totalmente fechados.
Observação
Ao usar a ferramenta Svcutil.exe, essa referência pode ser realizada usando o comutador de linha de comando /collectionType (forma curta: /ct). Lembre-se de que você também deve especificar o assembly para os tipos de coleção de referência usando o comutador /reference (forma curta: /r). Se o tipo for genérico, ele deverá ser seguido por uma aspa de fundo e o número de parâmetros genéricos. O acento grave (`) não deve ser confundido com o caractere de aspa única (‘). Você pode especificar vários tipos de coleção referenciados usando a opção /collectionType mais de uma vez.
Por exemplo, fazer com que todas as listas sejam importadas como Genéricas List<T>.
svcutil.exe MyService.wsdl MyServiceSchema.xsd /r:C:\full_path_to_system_dll\System.dll /ct:System.Collections.Generic.List`1
Ao importar qualquer coleção, essa lista de tipos de coleção referenciados é verificada e a coleção de melhor correspondência é usada se uma for encontrada, seja como um tipo de membro de dados (para coleções não personalizadas) ou como um tipo base para derivar (para coleções personalizadas). Os dicionários somente correspondem a dicionários, enquanto as listas são correspondentes às listas.
Por exemplo, se você adicionar o Genérico BindingList<T> e Hashtable à lista de tipos referenciados, o código do cliente gerado para o exemplo anterior será semelhante ao seguinte.
[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
Você pode especificar tipos de interface de coleção como parte de seus tipos de coleção referenciados, mas não pode especificar tipos de coleção inválidos (como os que não têm nenhum Add método ou construtor público).
Um genérico fechado é considerado a melhor correspondência. (Tipos não genéricos são considerados equivalentes a genéricos fechados de Object). Por exemplo, se os tipos Genérico List<T> de DateTime, Genérico BindingList<T> (aberto genérico) e ArrayList forem os tipos de coleção referenciados, o seguinte será gerado.
[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
Para coleções de listas, há suporte apenas para os casos na tabela a seguir.
| Tipo referenciado | Interface implementada por tipo de referência | Exemplo | Tipo tratado como: |
|---|---|---|---|
| Não genérico ou genérico fechado (qualquer número de parâmetros) | Não genérico | MyType : IListou MyType<T> : IListonde T= int |
Genérico fechado de Object (por exemplo, IList<object>) |
| Não genérico ou genérico fechado (qualquer número de parâmetros que não correspondem necessariamente ao tipo da coleção) | Genérico restrito | MyType : IList<string>ou MyType<T> : IList<string> onde T=int |
Genérico fechado (por exemplo, IList<string>) |
| Genérico fechado com qualquer número de parâmetros | Abrir genérico usando qualquer um dos parâmetros do tipo | MyType<T,U,V> : IList<U>where T= int, U=string, V=bool |
Genérico fechado (por exemplo, IList<string>) |
| Genérico aberto com um parâmetro | Abrir genérico usando o parâmetro do tipo | MyType<T> : IList<T>, T está aberto |
Abra o genérico (por exemplo, IList<T>) |
Se um tipo implementar mais de uma interface de coleção de listas, as seguintes restrições se aplicarão:
Se o tipo implementar genérico IEnumerable<T> (ou suas interfaces derivadas) várias vezes para tipos diferentes, o tipo não será considerado um tipo de coleção referenciado válido e será ignorado. Isso é verdadeiro mesmo se algumas implementações forem inválidas ou usarem genéricos abertos. Por exemplo, um tipo que implementa o genérico IEnumerable<T> de
inte o genérico IEnumerable<T> de T nunca seria usado como uma coleção referenciada deintou de qualquer outro tipo, independentemente de o tipo possuir um métodoAddque aceiteintou um métodoAddque aceite um parâmetro do tipo T, ou ambos.Se o tipo implementar tanto uma interface de coleção genérica quanto IList, o tipo nunca será usado como um tipo de coleção referenciado, a menos que a interface de coleção genérica seja um genérico fechado do tipo Object.
Para coleções de dicionário, há suporte apenas para os casos na tabela a seguir.
| Tipo referenciado | Interface implementada por tipo de referência | Exemplo | Tipo tratado como |
|---|---|---|---|
| Não genérico ou genérico fechado (qualquer número de parâmetros) | IDictionary | MyType : IDictionaryou MyType<T> : IDictionary onde T=int |
Genérico fechado IDictionary<object,object> |
| Genérico fechado (qualquer quantidade de parâmetros) | IDictionary<TKey,TValue>, fechado | MyType<T> : IDictionary<string, bool> onde T=int |
Genérico fechado (por exemplo, IDictionary<string,bool>) |
| Genérico fechado (qualquer quantidade de parâmetros) | IDictionary<TKey,TValue> Genérico, um de chave ou valor é fechado, o outro é aberto e usa um dos parâmetros do tipo | MyType<T,U,V> : IDictionary<string,V> where T=int, U=float,V=boolou MyType<Z> : IDictionary<Z,bool> onde Z=string |
Genérico fechado (por exemplo, IDictionary<string,bool>) |
| Genérico fechado (qualquer quantidade de parâmetros) | Genérico IDictionary<TKey,TValue>, tanto a chave quanto o valor são abertos e cada um usa um dos parâmetros do tipo. | MyType<T,U,V> : IDictionary<V,U> where T=int, U=bool, V=string |
Genérico fechado (por exemplo, IDictionary<string,bool>) |
| Abrir genérico (dois parâmetros) | Genérico IDictionary<TKey,TValue>, aberto, usa ambos os parâmetros genéricos do tipo na ordem em que aparecem | MyType<K,V> : IDictionary<K,V>, K e V estão abertos |
Abra o genérico (por exemplo, IDictionary<K,V>) |
Se o tipo implementar tanto IDictionary e IDictionary<TKey,TValue> Genérico, somente IDictionary<TKey,TValue> Genérico será considerado.
Não há suporte para a referência de tipos genéricos parciais.
Duplicatas não são permitidas. Por exemplo, você não pode adicionar o Genérico List<T> de Integer e a Coleção Genérica de Integer em ReferencedCollectionTypes, porque isso torna impossível determinar qual deles usar quando uma lista de inteiros é encontrada no esquema. As duplicatas serão detectadas somente se houver um tipo no esquema que exponha o problema de duplicatas. Por exemplo, se o esquema que está sendo importado não contiver listas de inteiros, é permitido ter tanto o Genérico List<T> quanto a Coleção Genérica de Integer no Integer, mas nenhum deles terá efeito.
Regras avançadas de coleção
Serializando coleções
Veja a seguir uma lista de regras de coleção para serialização:
É permitida a combinação de tipos de coleção (com coleções de coleções). Matrizes irregulares são tratadas como coleções de coleções. Não há suporte para matrizes multidimensionais.
Matrizes de bytes e matrizes de XmlNode são tipos especiais de matriz que são tratados como primitivos, não como coleções. Serializar uma matriz de bytes resulta em um único elemento XML que contém um pedaço de dados codificados em Base64, em vez de um elemento separado para cada byte. Para obter mais informações sobre como uma matriz de XmlNode é tratada, consulte XML e tipos de ADO.NET em contratos de dados. É claro que esses tipos especiais podem participar de coleções: uma matriz de matriz de bytes resulta em vários elementos XML, com cada um contendo uma parte dos dados codificados na Base64.
Se o DataContractAttribute atributo for aplicado a um tipo de coleção, o tipo será tratado como um tipo de contrato de dados regular, não como uma coleção.
Se um tipo de coleção implementar a IXmlSerializable interface, as seguintes regras serão aplicadas, considerando um tipo
myType:IList<string>, IXmlSerializable:Se o tipo declarado for
IList<string>, o tipo será serializado como uma lista.Se o tipo declarado for
myType, o tipo será serializado comoIXmlSerializable.Se o tipo declarado for
IXmlSerializable, ele será serializado comoIXmlSerializable, mas somente se você adicionarmyTypeà lista de tipos conhecidos.
As coleções são serializadas e desserializadas usando os métodos mostrados na tabela a seguir.
| Tipo de coleção implementa | Método(s) chamado(s) na serialização | Método(s) chamado(s) na desserialização |
|---|---|---|
| IDictionary<TKey,TValue>genérico | get_Keys, get_Values |
Adicionar genérico |
| IDictionary | get_Keys, get_Values |
Add |
| IList<T>genérico | Indexador genérico IList<T> | Adicionar genérico |
| ICollection<T>genérico | Enumerador | Adicionar genérico |
| IList | IList Indexador | Add |
| IEnumerable<T>genérico | GetEnumerator |
Um método não estático chamado Add que usa um parâmetro do tipo apropriado (o tipo do parâmetro genérico ou um de seus tipos base). Esse método deve existir para o serializador tratar um tipo de coleção como uma coleção durante a serialização e a desserialização. |
| IEnumerable (e, portanto ICollection, que deriva dele) | GetEnumerator |
Um método não estático chamado Add que usa um parâmetro do tipo Object. Esse método deve existir para o serializador tratar um tipo de coleção como uma coleção durante a serialização e a desserialização. |
A tabela anterior lista as interfaces de coleção em ordem de precedência decrescente. Isso significa, por exemplo, que, se um tipo implementa ambos IList e Genérico IEnumerable<T>, a coleção é serializada e desserializada de acordo com as IList regras:
Na desserialização, todas as coleções são desserializadas criando primeiro uma instância do tipo chamando o construtor sem parâmetros, que deve estar presente para o serializador tratar um tipo de coleção como uma coleção durante a serialização e a desserialização.
Se a mesma interface de coleção genérica for implementada mais de uma vez (por exemplo, se um tipo implementar tanto genérico ICollection<T> de
Integerquanto genérico ICollection<T> de String) e nenhuma interface de precedência superior for encontrada, a coleção não será tratada como uma coleção válida.Os tipos de coleção podem ter o SerializableAttribute atributo aplicado a eles e podem implementar a ISerializable interface. Ambos são ignorados. No entanto, se o tipo não atender totalmente aos requisitos de tipo de coleção (por exemplo, o
Addmétodo está ausente), o tipo não será considerado um tipo de coleção e, portanto, o SerializableAttribute atributo e a ISerializable interface serão usados para determinar se o tipo pode ser serializado.Aplicar o CollectionDataContractAttribute atributo a uma coleção para personalizá-lo remove o SerializableAttribute mecanismo de fallback anterior. Em vez disso, se uma coleção personalizada não atender aos requisitos de tipo de coleção, uma exceção InvalidDataContractException será gerada. A cadeia de caracteres de exceção geralmente contém informações que explicam por que um determinado tipo não é considerado uma coleção válida (nenhum
Addmétodo, nenhum construtor sem parâmetros e assim por diante), portanto, muitas vezes é útil aplicar o CollectionDataContractAttribute atributo para fins de depuração.
Nomeação de Coleção
Veja a seguir uma lista de regras de nomenclatura da coleção:
O namespace padrão para todos os contratos de dados de coleta de dicionário, bem como para contratos de dados de coleta de listas que contêm tipos primitivos, é
http://schemas.microsoft.com/2003/10/Serialization/Arrays, a menos que substituído usando o Namespace. Os tipos mapeados para tipos XSD integrados, bem como os tiposchar,TimespaneGuid, são considerados primitivos para essa finalidade.O namespace padrão para tipos de coleção que contêm tipos não primitivos, a menos que seja substituído usando o Namespace, é o mesmo que o namespace de contrato de dados do tipo contido na coleção.
O nome padrão para contratos de dados de coleção de listas, a menos que substituído usando Name, é a cadeia de caracteres "ArrayOf" combinada com o nome do contrato de dados do tipo contido na coleção. Por exemplo, o nome do contrato de dados para uma lista genérica de inteiros é "ArrayOfint". Tenha em mente que o nome do contrato de dados de
Objecté "anyType", portanto, o nome do contrato de dados de listas não genéricas, como ArrayList, é "ArrayOfanyType".
O nome padrão para contratos de dados de coleções de dicionário, a menos que sejam substituídos usando Name, é a sequência de caracteres "ArrayOfKeyValueOf", combinada com o nome do contrato de dados do tipo de chave, seguido pelo nome do contrato de dados do tipo de valor. Por exemplo, o nome do contrato de dados para um Dicionário Genérico de Cadeia de Caracteres e Inteiro é "ArrayOfKeyValueOfstringint". Além disso, se os tipos de valor ou chave não forem tipos primitivos, um hash de namespace para os namespaces de contrato de dados dos tipos de valor e chave será acrescentado ao nome. Para obter mais informações sobre hash de namespace, consulte Nomes de contratos de dados.
Cada contrato de dados de coleta de dicionário tem um contrato de dados complementar que representa uma entrada no dicionário. Seu nome é o mesmo que para o contrato de dados de dicionário, exceto para o prefixo "ArrayOf", e seu namespace é o mesmo que para o contrato de dados de dicionário. Por exemplo, para o contrato de dados de dicionário "ArrayOfKeyValueOfstringint", o contrato de dados "KeyValueofstringint" representa uma entrada no dicionário. Você pode personalizar o nome desse contrato de dados usando a ItemName propriedade, conforme descrito na próxima seção.
As regras de nomenclatura de tipo genérico, conforme descrito em Nomes de Contrato de Dados, aplicam-se totalmente aos tipos de coleção. Ou seja, você pode usar entre chaves no Nome para indicar parâmetros de tipo genérico. No entanto, os números dentro dos colchetes referem-se a parâmetros genéricos e não aos tipos contidos na coleção.
Personalização da coleção
Os seguintes usos do CollectionDataContractAttribute atributo são proibidos e resultam em uma exceção InvalidDataContractException :
Aplicando o DataContractAttribute atributo a um tipo ao qual o CollectionDataContractAttribute atributo foi aplicado ou a um de seus tipos derivados.
Aplicando o CollectionDataContractAttribute atributo a um tipo que implementa a IXmlSerializable interface.
Aplicando o CollectionDataContractAttribute atributo a um tipo que não seja de coleção.
Tentando definir KeyName ou ValueName em um atributo CollectionDataContractAttribute aplicado a um tipo não-dicionário.
Regras de polimorfismo
Como mencionado anteriormente, personalizar coleções usando o CollectionDataContractAttribute atributo pode interferir na intercambiabilidade de coleção. Dois tipos de coleção personalizados só poderão ser considerados equivalentes se o nome, o namespace, o nome do item, bem como os nomes de chave e valor (se forem coleções de dicionário) corresponderem.
Devido a personalizações, é possível usar inadvertidamente um contrato de dados de coleção no lugar de outro. Isso deve ser evitado. Consulte os tipos a seguir.
[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
Nesse caso, uma instância de Marks1 pode ser atribuída a testMarks. No entanto, Marks2 não deve ser usado porque seu contrato de dados não é considerado equivalente ao IList<int> contrato de dados. O nome do contrato de dados é "Marks2" e não "ArrayOfint", e o nome do elemento recorrente é "<mark>" e não "<int>".
As regras na tabela a seguir se aplicam à atribuição polimórfica de coleções.
| Tipo declarado | Atribuindo uma coleção não personalizada | Atribuindo uma coleção personalizada |
|---|---|---|
| Objeto | O nome do contrato é serializado. | O nome do contrato é serializado. A personalização é usada. |
| Interface de coleção | O nome do contrato não é serializado. | O nome do contrato não é serializado. A personalização não é usada.* |
| Coleção não personalizada | O nome do contrato não é serializado. | O nome do contrato é serializado. A personalização é usada.** |
| Coleção personalizada | O nome do contrato é serializado. A personalização não é usada.** | O nome do contrato é serializado. A personalização do tipo atribuído é usada.** |
*Com a classe NetDataContractSerializer, a personalização é usada nesse caso. A NetDataContractSerializer classe também serializa o nome do tipo real nesse caso, portanto, a desserialização funciona conforme o esperado.
**Esses casos geram instâncias inválidas de esquema e, portanto, devem ser evitados.
Nos casos em que o nome do contrato é serializado, o tipo de coleção atribuído deve estar na lista de tipos conhecidos. O oposto também é verdadeiro: nos casos em que o nome não é serializado, não é necessário adicionar o tipo à lista de tipos conhecidos.
Uma matriz de um tipo derivado pode ser atribuída a uma matriz de um tipo base. Nesse caso, o nome do contrato para o tipo derivado é serializado para cada elemento recorrente. Por exemplo, se um tipo Book deriva do tipo LibraryItem, você pode atribuir uma matriz de Book a uma matriz de LibraryItem. Isso não se aplica a outros tipos de coleção. Por exemplo, você não pode atribuir um Generic List of Book a um Generic List of LibraryItem. No entanto, você pode atribuir um elemento Generic List of LibraryItem que tenha instâncias de Book. No caso de matriz e não matriz, Book deve estar na lista de tipos conhecidos.
Coleções e preservação de referência de objeto
Quando um serializador funciona em um modo em que preserva referências de objeto, a preservação da referência de objeto também se aplica a coleções. Especificamente, a identidade do objeto é preservada para coleções inteiras e itens individuais contidos em coleções. Para dicionários, a identidade do objeto é preservada tanto para os objetos de par chave/valor quanto para os objetos de chave e valor individuais.