Condividi tramite


Tipi di insiemi nei contratti dati

Un insieme costituisce un elenco di elementi di un certo tipo. In .NET Framework tali elenchi possono essere rappresentati mediante matrici o una varietà di altri tipi (elenco generico, BindingList generico, StringCollection o ArrayList). Un insieme, ad esempio, può contenere un elenco di indirizzi per un determinato cliente Questi insiemi vengono denominati insiemi di elenchi, indipendentemente dal tipo effettivo.

Esiste un insieme speciale che rappresenta un'associazione tra un elemento (la "chiave") e un altro elemento (il "valore"). In .NET Framework questo insieme è rappresentato da tipi quali Hashtable e il dizionario generico. Un insieme di associazioni, ad esempio, può mappare una città (la "chiave") alla relativa popolazione (il "valore") Questi insiemi vengono denominati insiemi di dizionario, indipendentemente dal tipo effettivo.

Gli insiemi ricevono un trattamento speciale nel modello del contratto dati.

I tipi che implementano l'interfaccia IEnumerable, tra cui matrici e insiemi generici, vengono riconosciuti come insiemi. Di questi, i tipi che implementano l'interfaccia IDictionary o l'interfaccia IDictionary generica sono insiemi di dizionario mentre tutti gli altri sono insiemi di elenco.

Nelle sezioni seguenti vengono descritti dettagliatamente gli ulteriori requisiti relativi ai tipi di insiemi, ad esempio la presenza di un metodo denominato Add e di un costruttore predefinito. La presenza di questi requisiti garantisce che i tipi di insiemi possano essere serializzati e deserializzati e implica inoltre che alcuni insiemi non sono supportati direttamente, ad esempio l'elemento ReadOnlyCollection generico (perché non dispone di un costruttore predefinito) o Stack (perché il metodo per l'aggiunta di voci è denominato Push anziché Add). Per informazioni su come evitare queste restrizioni, tuttavia, vedere la sezione "Utilizzo di tipi di interfacce di insiemi e insiemi di sola lettura" più avanti in questo argomento.

I tipi contenuti negli insiemi devono essere tipi di contratto dati o devono poter essere serializzati in altro modo. Per ulteriori informazioni, vedere Tipi supportati dal serializzatore dei contratti dati.

Per ulteriori informazioni sulla distinzione tra insieme valido e insieme non valido, nonché sulla modalità di serializzazione degli insiemi, vedere le informazioni sulla serializzazione degli insiemi nella sezione "Regole avanzate di inserimento in insiemi" di questo argomento.

Insiemi intercambiabili

Si suppone che tutti gli insiemi di elenco dello stesso tipo dispongano dello stesso contratto dati (a meno che non vengano personalizzati con l'attributo CollectionDataContractAttribute, come viene descritto più avanti in questo argomento). Pertanto, ad esempio, i contratti dati seguenti sono equivalenti.

Entrambi i contratti dati generano XML simile al codice seguente.

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

L'intercambiabilità degli insiemi consente di utilizzare, ad esempio, un tipo di insieme ottimizzato per le prestazioni nel server e un tipo di insieme progettato per essere associato ai componenti dell'interfaccia utente nel client.

Come per gli insiemi di elenco, si suppone che tutti gli insiemi di dizionario con lo stesso tipo di chiave e valore dispongano dello stesso contratto dati (a meno che non vengano personalizzati con l'attributo CollectionDataContractAttribute).

Ai fini dell'equivalenza degli insiemi vale solo il tipo di contratto dati, non i tipi .NET. Un insieme Type1 è considerato equivalente a un insieme Type2 se Type1 e Type2 presentano contratti dati equivalenti.

Si suppone che insiemi non generici dispongano dello stesso contratto dati di insiemi generici di tipo Object. Ad esempio, i contratti dati per ArrayList e per un oggetto List generico di Object sono uguali.

Utilizzo di tipi di interfacce di insiemi e insiemi di sola lettura

Anche per i tipi di interfaccia di insieme (IEnumerable, IDictionary, IDictionary generico o interfacce che derivano da queste interfacce) si suppone che dispongano di contratti dati di insieme, equivalenti a contratti dati di insieme per tipi di insieme effettivi. È dunque possibile dichiarare il tipo serializzato come tipo di interfaccia di insieme e i risultati sono gli stessi che verrebbero ottenuti se fosse utilizzato un tipo di insieme effettivo. I contratti dati seguenti, ad esempio, sono equivalenti.

Durante la serializzazione, quando il tipo dichiarato è un'interfaccia, il tipo dell'istanza effettivo utilizzato può essere qualsiasi tipo che implementa quell'interfaccia. Le restrizioni discusse in precedenza (la presenza di un costruttore predefinito e di un metodo Add) non sono applicabili. È ad esempio possibile impostare indirizzi in Customer2 su un'istanza della classe ReadOnlyCollection generica di Address, anche se non si può dichiarare direttamente un membro dati di tipo ReadOnlyCollection generico.

Durante la deserializzazione, quando il tipo dichiarato è un'interfaccia, il motore di serializzazione sceglie un tipo che implementi l'interfaccia dichiarata e viene creata un'istanza del tipo. Il meccanismo dei tipi conosciuti (descritto in Tipi conosciuti di contratto dati) non esercita effetti in questo caso. La scelta del tipo è incorporata in WCF.

Personalizzazione dei tipi di insiemi

È possibile personalizzare i tipi di insiemi utilizzando l'attributo CollectionDataContractAttribute, che presenta molti utilizzi.

Si noti che la personalizzazione dei tipi di insieme compromette l'intercambiabilità degli insiemi, pertanto si consiglia in genere di evitare l'applicazione di questo attributo quando possibile. Per ulteriori informazioni su questo problema, vedere la sezione "Regole avanzate di inserimento in insiemi" più avanti in questo argomento.

Denominazione del contratto dati dell'insieme

Le regole per la denominazione dei tipi di insiemi sono simili alle regole di denominazione dei tipi di contratto dati normali, come viene descritto in Nomi di contratto dati, nonostante alcune importanti differenze:

  • Per personalizzare il nome viene utilizzato l'attributo CollectionDataContractAttribute anziché l'attributo DataContractAttribute. Anche l'attributo CollectionDataContractAttribute dispone delle proprietà Name e Namespace.

  • Quando l'attributo CollectionDataContractAttribute non viene applicato, il nome e lo spazio dei nomi predefiniti per i tipi di insieme dipendono dai nomi e dagli spazi dei nomi dei tipi contenuti nell'insieme. Non vengono influenzati dal nome e dallo spazio dei nomi del tipo di insieme stesso. Per un esempio, vedere i tipi seguenti.

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

Il nome del contratto dati di entrambi i tipi è "ArrayOfstring" e non "CustomerList1" o "StringList1". Ciò significa che eseguendo la serializzazione di uno qualsiasi di questi tipi a livello di radice si ottiene codice XML simile al seguente:

<ArrayOfstring>
    <string>…</string>
    <string>…</string>
    <string>…</string>
    …
</ArrayOfstring>

Questa regola di denominazione è stata scelta per avere la certezza che i tipi non personalizzati che rappresentano un elenco di stringhe abbiano lo stesso contratto dati e la stessa rappresentazione XML. In questo modo l'intercambiabilità degli insiemi è possibile. Nell'esempio CustomerList1 e StringList1 sono completamente intercambiabili.

Quando viene applicato l'attributo CollectionDataContractAttribute, tuttavia, l'insieme diventa un contratto dati di un insieme personalizzato, anche se nell'attributo non è impostata alcuna proprietà. Il nome e lo spazio dei nomi del contratto dati dell'insieme dipendono quindi dal tipo di insieme stesso. Per un esempio, vedere il tipo seguente.

Quando viene serializzato, l'XML risultante è simile al codice seguente.

<CustomerList2>
    <string>…</string>
    <string>…</string>
    <string>…</string>
    …
</CustomerList2>

Si noti che questo codice non è più equivalente alla rappresentazione XML dei tipi non personalizzati.

  • È quindi possibile utilizzare le proprietà Name e Namespace per personalizzare ulteriormente la denominazione. Fare riferimento alla classe seguente.

L'XML risultante è simile al codice seguente.

<cust_list>
    <string>…</string>
    <string>…</string>
    <string>…</string>
    …
</cust_list>

Per ulteriori informazioni, vedere la sezione "Regole avanzate di inserimento in insiemi" più avanti in questo argomento.

Personalizzazione del nome dell'elemento ripetuto negli insiemi di elenco

Gli insiemi di elenco contengono voci ripetute. Normalmente ogni voce ripetuta è rappresentata come un elemento denominato secondo il nome del contratto dati del tipo contenuto nell'insieme.

Negli esempi CustomerList, gli insiemi contengono stringhe. Poiché il nome del contratto dati per il tipo primitivo della stringa è "string", l'elemento ripetuto è "<string>".

Utilizzando la proprietà ItemName nell'attributo CollectionDataContractAttribute, tuttavia, questo nome di elemento ripetuto può essere personalizzato. Per un esempio, vedere il tipo seguente.

L'XML risultante è simile al codice seguente.

<CustomerList4>
    <customer>…</ customer>
    <customer>…</customer>
    <customer>…</customer>
    …
</CustomerList4>

Lo spazio dei nomi dell'elemento ripetuto è sempre uguale allo spazio dei nomi del contratto dati dell'insieme, che può essere personalizzato tramite la proprietà Namespace, come descritto in precedenza.

Personalizzazione di insiemi di dizionario

Gli insiemi di dizionario sono essenzialmente elenchi di voci, in cui ogni voce presenta una chiave seguita da un valore. Come per gli elenchi normali, è possibile modificare il nome di elemento corrispondente all'elemento ripetuto utilizzando la proprietà ItemName.

È inoltre possibile modificare i nomi di elemento che rappresentano la chiave e il valore utilizzando le proprietà KeyName e ValueName. Gli spazi dei nomi di questi elementi sono gli stessi dello spazio dei nomi del contratto dati dell'insieme.

Per un esempio, vedere il tipo seguente.

Quando viene serializzato, l'XML risultante è simile al codice seguente.

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

Per ulteriori informazioni sugli insiemi di dizionario, vedere la sezione "Regole avanzate di inserimento in insiemi" più avanti in questo argomento.

Insiemi e tipi conosciuti

Non è necessario aggiungere tipi di insieme a tipi conosciuti quando vengono utilizzati polimorficamente in luogo di altri insiemi o di altre interfacce di insieme. Se ad esempio si dichiara un membro dati di tipo IEnumerable e lo si utilizza per inviare un'istanza di ArrayList, non è necessario aggiungere ArrayList a tipi conosciuti.

Quando si utilizzano insiemi in modo polimorfico al posto di tipi diversi da insiemi, è necessario aggiungerli ai tipi conosciuti. Ad esempio, se si dichiara un membro dati di tipo Object e lo si utilizza per inviare un'istanza di ArrayList, aggiungere ArrayList ai tipi conosciuti.

Ciò non consente di serializzare eventuali insiemi equivalenti polimorficamente. Ad esempio, quando si aggiunge ArrayList all'elenco di tipi conosciuti nell'esempio precedente, non è consentito assegnare la classe Array of Object, anche se presenta un contratto dati equivalente. Il comportamento normale di tipi conosciuti nel caso della serializzazione per tipi diversi da insiemi non è diverso, ma è particolarmente importante essere a conoscenza di tale funzionamento nel caso degli insiemi perché capita frequentemente che si equivalgano.

Durante la serializzazione solo un tipo può essere conosciuto in un determinato ambito per un contratto dati specificato e gli insiemi equivalenti presentano tutti gli stessi contratti dati. Nell'esempio precedente, ciò significa che non è possibile aggiungere sia ArrayList sia Array of Object ai tipi conosciuti nello stesso ambito. Anche questo caso è equivalente al comportamento dei tipi conosciuti per tipi diversi da insiemi ma è particolarmente importante esserne a conoscenza per quanto riguarda gli insiemi.

È inoltre possibile che i tipi conosciuti siano necessari come contenuto di insiemi. Se un elemento ArrayList, ad esempio, contiene effettivamente istanze di Type1 e Type2, entrambi questi tipi devono essere aggiunti ai tipi conosciuti.

Nell'esempio seguente viene illustrato un oggetto grafico costruito correttamente in cui sono utilizzati insiemi e tipi conosciuti L'esempio è stato studiato appositamente in quanto in un'applicazione effettiva i membri dati seguenti non verrebbero normalmente definiti Object e quindi non si verificherebbero problemi di polimorfismo/tipi conosciuti.

In fase di deserializzazione, se il tipo dichiarato è un tipo di insieme, ne viene creata un'istanza indipendentemente dal tipo effettivamente inviato. Se il tipo dichiarato è un'interfaccia di insieme, il deserializzatore sceglie un tipo di cui creare un'istanza senza considerare i tipi conosciuti.

In fase di deserializzazione, inoltre, se il tipo dichiarato non è un tipo di insieme ma viene inviato un tipo di insieme, dall'elenco dei tipi conosciuti viene selezionato un tipo di insieme corrispondente. In fase di deserializzazione è possibile aggiungere tipi di interfaccia di insieme all'elenco di tipi conosciuti. Anche in questo caso il motore di deserializzazione sceglie un tipo di cui creare un'istanza.

Insiemi e classe NetDataContractSerializer

Quando si utilizza la classe NetDataContractSerializer, i tipi di insieme non personalizzati (senza l'attributo CollectionDataContractAttribute) che non sono matrici perdono il relativo significato speciale.

Tipi di insieme non personalizzati contrassegnati con l'attributo SerializableAttribute possono comunque essere serializzati dalla classe NetDataContractSerializer secondo l'attributo SerializableAttribute o le regole dell'interfaccia ISerializable.

Tipi di insieme personalizzati, interfacce di insieme e matrici vengono comunque considerati insiemi, anche quando la classe NetDataContractSerializer è in uso.

Insiemi e schema

Tutti gli insiemi equivalenti presentano la stessa rappresentazione nello schema XSD (XML Schema Definition Language). Per questo motivo in genere non si ottiene, nel codice client generato, lo stesso tipo di insieme del codice generato nel server. Il server, ad esempio, può utilizzare un contratto dati con List generico del membro dati Integer ma nel codice client generato lo stesso membro dati può diventare una matrice di numeri interi.

Gli insiemi di dizionario sono contrassegnati con un'annotazione dello schema specifica di WCF che indica che si tratta di dizionari. In caso contrario, non sarebbero distinguibili da semplici elenchi contenenti voci con una chiave e un valore. Per una descrizione esatta del modo in cui gli insiemi sono rappresentati nello schema del contratto dati, vedere Riferimento allo schema del contratto dati.

Per impostazione predefinita i tipi non vengono generati per insiemi non personalizzati nel codice importato. I membri dati di tipi di insiemi di elenco sono importati come matrici e i membri dati di tipi di insiemi di dizionario sono importati come dizionario generico.

Per gli insiemi personalizzati, tuttavia, vengono generati tipi separati, contrassegnati con l'attributo CollectionDataContractAttribute (il tipo di insieme personalizzato contenuto nello schema è un tipo che non utilizza spazio dei nomi, nome, elemento ripetuto o nomi di elementi chiave/valore predefiniti). Si tratta di tipi vuoti che derivano da List generico per i tipi di elenco e dal dizionario generico per i tipi di dizionario.

È ad esempio possibile che nel server siano presenti i tipi seguenti.

Quando lo schema viene esportato per poi essere nuovamente importato, il codice client generato è simile al seguente (per facilità di lettura vengono mostrati i campi anziché le proprietà):

È consigliabile utilizzare tipi diversi nel codice generato anziché i tipi predefiniti. È ad esempio opportuno utilizzare BindingList generico anziché matrici normali dei membri dati per semplificarne l'associazione ai componenti dell'interfaccia utente.

Per scegliere i tipi di insieme da generare, durante l'importazione dello schema passare un elenco dei tipi di insieme che si desidera utilizzare nella proprietà ReferencedCollectionTypes dell'oggetto ImportOptions. Questi tipi sono chiamati tipi di insieme a cui viene fatto riferimento.

Quando si fa riferimento a tipi generici, deve trattarsi di generics completamente aperti o di generics completamente chiusi.

Nota

Quando si utilizza lo strumento Svcutil.exe, questo riferimento può essere eseguito utilizzando l'opzione della riga di comando /collectionType (forma breve /ct). È importante ricordare che è anche necessario specificare l'assembly per i tipi di insieme a cui viene fatto riferimento utilizzando l'opzione /reference (forma breve /r). Se il tipo è generico, deve essere seguito da una virgoletta inversa e dal numero di parametri generici. La virgoletta inversa (`) non deve essere confusa con il carattere della virgoletta singola ('). È possibile specificare più tipi di insieme a cui viene fatto riferimento utilizzando l'opzione /collectionType più di una volta.

Ad esempio, per fare in modo che tutti gli elenchi vengano importati come oggetto List generico.

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

Quando si importa un insieme, l'elenco di tipi di insieme a cui si fa riferimento viene analizzato e viene utilizzato l'insieme con la migliore corrispondenza trovato, come tipo di membro dati (per gli insiemi non personalizzati) o come tipo di base per la derivazione (per gli insiemi personalizzati). I dizionari vengono associati solo a dizionari mentre gli elenchi a elenchi.

Se ad esempio si aggiungono la classe BindingList generica e la classe Hashtable all'elenco di tipi a cui viene fatto riferimento, il codice client generato per l'esempio precedente sarà simile al seguente:

È possibile specificare i tipi di interfaccia di insieme nell'ambito dei tipi di insieme a cui si fa riferimento, ma non è possibile specificare tipi di insieme non validi (ad esempio tipi senza il metodo Add o il costruttore pubblico).

Un generico chiuso è considerato la corrispondenza migliore (i tipi non generici sono considerati equivalenti ai generics chiusi di Object). Se ad esempio i tipi List generico di DateTime, BindingList generico (generico aperto) e ArrayList sono tipi di insieme a cui viene fatto riferimento, viene generato il codice seguente:

Per gli insiemi di elenchi, sono supportati solo i casi illustrati nella tabella seguente:

Tipo a cui viene fatto riferimento Interfaccia implementata dal tipo a cui viene fatto riferimento Esempio Tipo trattato come:

Non generico o generico chiuso (qualsiasi numero di parametri)

Non generico

MyType : IList

o

MyType<T> : IList

dove T= int

Generico chiuso di Object (ad esempio, IList<object>)

Non generico o generico chiuso (qualsiasi numero di parametri che non corrispondono necessariamente al tipo di insieme)

Generico chiuso

MyType : IList<string>

oppure

MyType<T> : IList<string> dove T=int

Generico chiuso (ad esempio IList<string>)

Generico chiuso con qualsiasi numero di parametri

Generico aperto che utilizza qualsiasi parametro del tipo

MyType<T,U,V> : IList<U>

dove T=int, U=string, V=bool

Generico chiuso (ad esempio IList<string>)

Generico aperto con un parametro

Generico aperto che utilizza il parametro del tipo

MyType<T> : IList<T>, T è aperto

Generico aperto (ad esempio IList<T>)

Se un tipo implementa più di un'interfaccia dell'insieme di elenco, vengono applicate le restrizioni seguenti:

  • Se il tipo implementa IEnumerable generico (o le interfacce derivate) più volte per tipi diversi, non viene considerato un tipo di insieme a cui fare riferimento valido e viene ignorato. Questa condizione è vera anche se alcune implementazioni non sono valide o utilizzano generics aperti. Un tipo che implementa IEnumerable generico di int e IEnumerable generico di T, ad esempio, non verrebbe mai utilizzato come insieme a cui si fa riferimento di int o di qualsiasi altro tipo, indipendentemente dalla circostanza che il tipo abbia un metodo Add che accetta int o un metodo Add che accetta un parametro di tipo T o entrambi.
  • Se il tipo implementa un'interfaccia di insieme generico oltre a IList, non viene mai utilizzato come tipo di insieme a cui si fa riferimento, a meno che l'interfaccia di insieme generico non sia un generico chiuso di tipo Object.

Per gli insiemi di dizionario, sono supportati solo i casi illustrati nella tabella seguente:

Tipo a cui viene fatto riferimento Interfaccia implementata dal tipo a cui viene fatto riferimento Esempio Tipo trattato come

Non generico o generico chiuso (qualsiasi numero di parametri)

IDictionary

MyType : IDictionary

oppure

MyType<T> : IDictionary dove T=int

Generico chiuso IDictionary<object,object>

Generico chiuso (qualsiasi numero di parametri)

IDictionary, chiuso

MyType<T> : IDictionary<string, bool> dove T=int

Generico chiuso (ad esempio IDIctionary<string,bool>)

Generico chiuso (qualsiasi numero di parametri)

IDictionary generico, la chiave o il valore è chiuso, l'altro è aperto e utilizza uno dei parametri del tipo

MyType<T,U,V> : IDictionary<string,V> dove T=int, U=float,V=bool

oppure

MyType<Z> : IDictionary<Z,bool> dove Z=string

Generico chiuso (ad esempio IDictionary<string,bool>)

Generico chiuso (qualsiasi numero di parametri)

IDictionary generico, chiave e valore sono aperti e ognuno utilizza uno dei parametri del tipo

MyType<T,U,V> : IDictionary<V,U> dove T=int, U=bool, V=string

Generico chiuso (ad esempio IDictionary<string,bool>)

Generico aperto (due parametri)

IDictionary generico, aperto, utilizza entrambi i parametri generici del tipo nell'ordine in cui sono visualizzati.

MyType<K,V> : IDictionary<K,V>, K e V entrambi aperti

Generico aperto (ad esempio IDictionary<K,V>)

Se il tipo implementa sia IDictionary che IDictionary generico, solo IDictionary generico viene considerato.

Il riferimento a tipi generici parziali non è supportato.

I duplicati non sono consentiti, ad esempio non è possibile aggiungere l'oggetto List generico di Integer e l'insieme generico di Integer a ReferencedCollectionTypes, perché ciò renderebbe impossibile stabilire quale utilizzare quando nello schema viene trovato un elenco di valori integer. I duplicati vengono rilevati solo se nello schema esiste un tipo che espone il problema dei duplicati. Se ad esempio lo schema importato non contiene elenchi di numeri interi, è consentito disporre sia di List generico di Integer che dell'insieme generico di Integer nella proprietà ReferencedCollectionTypes, ma nessuno esercita alcun effetto.

Regole avanzate di inserimento in insiemi

Serializzazione degli insiemi

Di seguito vengono elencate le regole per la serializzazione degli insiemi:

  • La combinazione di tipi di insieme (con insiemi di insiemi) è consentita. Le matrici di matrici vengono trattate come insiemi di insiemi. Le matrici multidimensionali non sono supportate.
  • Matrici di byte e matrici di XmlNode sono tipi speciali di matrici trattati come primitivi, non come insiemi. La serializzazione di una matrice di byte genera un singolo elemento XML contenente un blocco di dati con codifica Base64 anziché un elemento separato per ogni byte. Per ulteriori informazioni su come viene considerata una matrice di XmlNode, vedere Tipi XML e ADO.NET nei contratti dati. Questi tipi speciali, naturalmente, possono fare parte degli insiemi: una matrice di matrice di byte sfocia in più elementi XML, ognuno dei quali contiene un blocco di dati con codifica Base64.
  • Se l'attributo DataContractAttribute viene applicato a un tipo di insieme, il tipo viene trattato come tipo di contratto dati normale, non come un insieme.
  • Se un tipo implementa l'interfaccia IXmlSerializable, vengono applicate le regole seguenti, dato un tipo myType:IList<string>, IXmlSerializable:
    • Il tipo di dichiarato è IList<string>, il tipo è serializzato come elenco.
    • Il tipo di dichiarato è myType, ed è serializzato come IXmlSerializable.
    • Se il tipo dichiarato è IXmlSerializable, viene serializzato come IXmlSerializable, ma solo se si aggiunge myType all'elenco dei tipi noti.
  • Gli insiemi vengono serializzati e deserializzati tramite i metodi illustrati nella tabella seguente:
Il tipo di insieme implementa Metodo/i chiamato/i durante la serializzazione Metodo/i chiamato/i durante la deserializzazione

IDictionary generico

get_Keys, get_Values

Add generico

IDictionary

get_Keys, get_Values

Add

IList generico

Indicizzatore IList generico

Add generico

ICollection generico

Enumeratore

Add generico

IList

Indicizzatore IList

Add

IEnumerable generico

GetEnumerator

Metodo non statico denominato Add che accetta un parametro del tipo appropriato (il tipo del parametro generico o uno dei tipi di base). È necessario che tale metodo esista affinché il serializzatore tratti un tipo di insieme come insieme sia durante la serializzazione che durante la deserializzazione.

IEnumerable (e di conseguenza ICollection, interfaccia derivata)

GetEnumerator

Metodo non statico denominato Add che accetta un parametro di tipo Object. È necessario che tale metodo esista affinché il serializzatore tratti un tipo di insieme come insieme sia durante la serializzazione che durante la deserializzazione.

Nella tabella precedente sono elencate le interfacce di insieme in ordine decrescente di precedenza. Se un tipo implementa sia IList che IEnumerable generico, ad esempio, l'insieme viene serializzato e deserializzato secondo le regole di IList:

  • In fase di deserializzazione, tutti gli insiemi vengono deserializzati creando innanzitutto un'istanza del tipo chiamando il costruttore predefinito, che deve essere presente affinché il serializzatore sia in grado di trattare un tipo di insieme come insieme durante la serializzazione e la deserializzazione.
  • Se la stessa interfaccia di insieme generico viene implementata più di una volta (ad esempio se un tipo implementa sia ICollection generico di Integer che ICollection generico di String) e non viene trovata nessuna interfaccia con un livello di precedenza maggiore, l'insieme non viene trattato come insieme valido.
  • L'attributo SerializableAttribute può essere applicato ai tipi di insieme, i quali possono implementare l'interfaccia ISerializable. Entrambi vengono ignorati. Se, tuttavia, il tipo non soddisfa pienamente i requisiti del tipo di insieme (ad esempio, manca il metodo Add), il tipo non viene considerato un tipo di insieme, di conseguenza per stabilire se il tipo può essere serializzato, vengono utilizzati l'attributo SerializableAttribute e l'interfaccia ISerializable.
  • L'applicazione dell'attributo CollectionDataContractAttribute a un insieme per personalizzarlo rimuove il meccanismo di fallback di SerializableAttribute precedente. Se invece un insieme personalizzato non soddisfa i requisiti del tipo di insieme, viene generata un'eccezione InvalidDataContractException. Poiché spesso la stringa dell'eccezione contiene informazioni che spiegano il motivo per il quale un determinato tipo non viene considerato un insieme valido (nessun metodo Add, nessun costruttore predefinito e così via), risulta utile applicare l'attributo CollectionDataContractAttribute a scopo di debug.

Denominazione di insiemi

Di seguito vengono elencate le regole di denominazione degli insiemi:

  • Lo spazio dei nomi predefinito per tutti i contratti dati degli insiemi di dizionario, nonché per contratti dati degli insiemi di elenco contenenti tipi primitivi, è https://schemas.microsoft.com/2003/10/Serialization/Arrays, a meno che non venga sottoposto a override utilizzando Namespace. I tipi che eseguono il mapping a tipi XSD incorporati, nonché i tipi char, Timespan e Guid, vengono considerati primitivi a questo scopo.
  • Lo spazio dei nomi predefinito per tipi di insieme che contengono tipi non primitivi corrisponde allo spazio dei nomi del contratto dati del tipo contenuto nell'insieme, a meno che non venga eseguito l'override utilizzando Namespace.
  • Il nome predefinito per i contratti dati degli insiemi di elenco, a meno che non venga sottoposto a override utilizzando Name, è la stringa "ArrayOf" associata al nome del contratto dati del tipo contenuto nell'insieme. Il nome del contratto dati per un elenco generico di numeri interi è, ad esempio, "ArrayOfint". È importante ricordare che il nome del contratto dati di Object è "anyType", quindi il nome del contratto dati di elenchi non generici come ArrayList è "ArrayOfanyType".

Il nome predefinito per i contratti dati degli insiemi di dizionario, a meno che non venga sottoposto a override utilizzando Name, è la stringa "ArrayOfKeyValueOf" associata al nome del contratto dati del tipo di chiave seguito dal nome del contratto dati del tipo di valore. Il nome del contratto dati per un dizionario generico di stringa e numero intero, ad esempio, è "ArrayOfKeyValueOfstringint". Inoltre, se il tipo di chiave o il tipo di valore non sono tipi primitivi, un hash di spazio dei nomi degli spazi dei nomi del contratto dati di chiave e valore viene aggiunto al nome. Per ulteriori informazioni sugli hash di spazio dei nomi, vedere Nomi di contratto dati.

Ogni contratto dati dell'insieme di dizionario dispone di un contratto dati complementare che rappresenta una voce del dizionario. Il nome è lo stesso del contratto dati del dizionario, ad eccezione del prefisso "ArrayOf", e lo spazio dei nomi corrisponde a quello del contratto dati del dizionario. Per il contratto dati del dizionario "ArrayOfKeyValueOfstringint", ad esempio, il contratto dati "KeyValueofstringint" rappresenta una voce del dizionario. È possibile personalizzare il nome di questo contratto dati utilizzando la proprietà ItemName, come viene descritto nella prossima sezione.

Le regole di denominazione dei tipi generici, descritte in Nomi di contratto dati, si applicano completamente ai tipi di insieme, ovvero è possibile utilizzare parentesi graffe all'interno di Name per indicare parametri di tipi generici. Tuttavia, i numeri tra parentesi graffe si riferiscono a parametri generici e non a tipi contenuti nell'insieme.

Personalizzazione di insiemi

Gli utilizzi seguenti dell'attributo CollectionDataContractAttribute non sono consentiti e generano un'eccezione InvalidDataContractException:

  • Applicare l'attributo DataContractAttribute a un tipo al quale è stato applicato l'attributo CollectionDataContractAttribute oppure a uno dei tipi derivati.
  • Applicare l'attributo CollectionDataContractAttribute a un tipo che implementa l'interfaccia IXmlSerializable.
  • Applicare l'attributo CollectionDataContractAttribute a un tipo diverso da un insieme.
  • Tentare di impostare KeyName o ValueName su un attributo CollectionDataContractAttribute applicato a un tipo diverso da un dizionario.

Regole del polimorfismo

Come indicato in precedenza, la personalizzazione degli insiemi mediante l'attributo CollectionDataContractAttribute può interferire con l'intercambiabilità degli stessi. Due tipi di insiemi personalizzati possono essere considerati equivalenti solo se il nome, lo spazio dei nomi, il nome dell'elemento nonché il nome di chiave e valore (se insiemi di dizionari) corrispondono.

A causa delle personalizzazioni, è possibile utilizzare inavvertitamente il contratto dati di un insieme laddove ne è previsto un altro. Questa evenienza deve essere evitata. Vedere i tipi seguenti.

In questo caso, un'istanza di Marks1 può essere assegnata a testMarks. Marks2, tuttavia, non deve essere utilizzato poiché il relativo contratto dati non viene considerato equivalente al contratto dati IList<int>. Il nome del contratto dati è "Marks2" e non "ArrayOfint" e il nome dell'elemento ripetuto è "<mark>" e non "<int>".

Per l'assegnazione polimorfica degli insiemi vengono applicate le regole riportate nella tabella seguente:

Tipo dichiarato Assegnazione di un insieme non personalizzato Assegnazione di un insieme personalizzato

Oggetto

Il nome del contratto è serializzato.

Il nome del contratto è serializzato.

Viene utilizzata la personalizzazione.

Interfaccia di insieme

Il nome del contratto non è serializzato.

Il nome del contratto non è serializzato.

La personalizzazione non viene utilizzata.*

Insieme non personalizzato

Il nome del contratto non è serializzato.

Il nome del contratto è serializzato.

Viene utilizzata la personalizzazione.**

Insieme personalizzato

Il nome del contratto è serializzato. La personalizzazione non viene utilizzata.**

Il nome del contratto è serializzato.

Viene utilizzata la personalizzazione del tipo assegnato.**

*Con la classe NetDataContractSerializer, in questo caso viene utilizzata la personalizzazione. La classe NetDataContractSerializer serializza inoltre il nome effettivo del tipo in questo caso, quindi la deserializzazione viene eseguita in base alle previsioni.

**Questi casi determinano istanze non valide per lo schema, quindi devono essere evitati.

Nei casi in cui il nome del contratto è serializzato, il tipo di insieme assegnato deve risultare nell'elenco dei tipi conosciuti. È anche vero il contrario: nei casi in cui il nome non è serializzato, l'aggiunta del tipo all'elenco dei tipi conosciuti non è necessaria.

Una matrice di un tipo derivato può essere assegnata a una matrice di un tipo di base. In questo caso il nome del contratto per il tipo derivato viene serializzato per ogni elemento ripetuto. Se ad esempio un tipo Book deriva dal tipo LibraryItem, è possibile assegnare una matrice di Book a una matrice di LibraryItem. Quanto esposto sopra non vale per altri tipi di insieme. Ad esempio, non è possibile assegnare un oggetto Generic List of Book a un oggetto Generic List of LibraryItem. È tuttavia possibile assegnare un Generic List of LibraryItem contenente istanze di Book. In entrambi i casi, matrice e non matrice, Book deve essere presente nell'elenco dei tipi conosciuti.

Insiemi e conservazione dei riferimenti all'oggetto

Quando un serializzatore opera in una modalità che consente di preservare i riferimenti all'oggetto, la conservazione dei riferimenti all'oggetto si applica anche agli insiemi. In particolare, l'identità dell'oggetto viene conservata sia per insiemi interi che per elementi singoli contenuti negli insiemi. Per i dizionari, l'identità dell'oggetto viene conservata sia per oggetti coppia di chiave e valore che per oggetti chiave e valore singoli.

Vedere anche

Riferimenti

CollectionDataContractAttribute