Sammlungstypen in Datenverträgen

Eine Sammlung ist eine Liste von Elementen eines bestimmten Typs. In .NET Framework können solche Listen mithilfe von Arrays oder einer Vielzahl anderer Typen (generische Liste, generische BindingList<T>, StringCollection oder ArrayList) dargestellt werden. Eine Sammlung kann z. B. eine Liste von Adressen für einen bestimmten Kunden enthalten. Solche Sammlungen werden – unabhängig von ihrem tatsächlichen Typ – als Listensammlungen bezeichnet.

Es gibt eine spezielle Sammlungsform, die eine Zuordnung zwischen einem Element (dem "Schlüssel") und einem anderen Element (dem "Wert") darstellt. Im .NET Framework werden diese Sammlungen durch Typen wie Hashtable und das generische Wörterbuch dargestellt. Eine Zuordnungssammlung kann z. B. eine Stadt ("Schlüssel") der zugehörigen Bevölkerung ("Wert") zuordnen. Solche Sammlungen werden – unabhängig von ihrem tatsächlichen Typ – als Wörterbuchsammlungen bezeichnet.

Sammlungen werden im Datenvertragsmodell besonders behandelt.

Typen, die die IEnumerable-Schnittstelle einschließlich Arrays und generischen Sammlungen implementieren, werden als Sammlungen erkannt. Davon sind die Typen, die die Schnittstellen für das IDictionary oder das generische IDictionary<TKey,TValue> implementieren, Wörterbuchsammlungen. Alle anderen sind Listensammlungen.

Zusätzliche Anforderungen für Sammlungstypen, wie das Aufweisen einer Methode mit dem Namen Add und eines Standardkonstruktors, werden in den folgenden Abschnitten im Detail besprochen. Dies stellt sicher, dass Sammlungstypen sowohl serialisiert als auch deserialisiert sein können. Das bedeutet, dass einige Sammlungen nicht direkt unterstützt werden, z. B. die generische ReadOnlyCollection<T> (da sie über keinen Standardkonstruktor verfügt). Informationen zum Umgehen dieser Einschränkungen finden Sie im Abschnitt "Verwenden von Sammlungsschnittstellentypen und schreibgeschützten Sammlungen" weiter unten in diesem Thema.

Die in Sammlungen enthaltenen Typen müssen Datenvertragstypen oder anderweitig serialisierbar sein.Weitere Informationen finden Sie unter Vom Datenvertragsserialisierer unterstützte Typen.

Weitere Informationen finden Sie unter zu gültigen und ungültigen Sammlungen sowie Informationen zum Serialisieren von Sammlungen finden Sie in den Informationen zum Serialisieren von Sammlungen im Abschnitt "Erweiterte Sammlungsregeln" dieses Themas.

Austauschbare Sammlungen

Bei allen Listensammlungen des gleichen Typs wird davon ausgegangen, dass sie den gleichen Datenvertrag haben (es sei denn, sie wurden mit dem CollectionDataContractAttribute-Attribut individuell angepasst, wie weiter unten in diesem Thema beschrieben). Daher stimmen beispielsweise die folgenden Datenverträge überein:

[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

Beide Datenverträge führen zu XML-Code, ähnlich dem folgenden Code:

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

Die Austauschbarkeit von Sammlungen ermöglicht Ihnen beispielsweise die Verwendung eines für die Leistung auf dem Server optimierten Sammlungstyps sowie eines Sammlungstyps, der für die Bindung an Benutzerschnittstellenkomponenten auf dem Client entwickelt wurde.

Ähnlich wie bei den Listensammlungen haben alle Wörterbuchsammlungen, die die gleichen Schlüssel- und Werttypen aufweisen, den gleichen Datenvertrag (es sei denn, sie wurden mit dem CollectionDataContractAttribute-Attribut individuell angepasst).

Nur der Datenvertragstyp ist, was die Sammlungsäquivalenz betrifft, wichtig, nicht die .NET-Typen, d. h. eine Sammlung vom Typ1 gilt als äquivalent zu einer Sammlung vom Typ2, wenn Typ1 und Typ2 die gleichen Datenverträge aufweisen.

Bei nicht generischen Sammlungen wird davon ausgegangen, dass sie den gleichen Datenvertrag wie generische Sammlungen des Typs Object aufweisen. (Beispielsweise sind die Datenverträge für die ArrayList und die generische List<T> von Object gleich.)

Verwenden von Sammlungsschnittstellentypen und schreibgeschützten Sammlungen

Bei Sammlungsschnittstellentypen (IEnumerable, IDictionary, generische IDictionary<TKey,TValue> oder von diesen Schnittstellen abgeleiteten Schnittstellen) wird ebenfalls davon ausgegangen, dass sie die gleichen Sammlungsdatenverträge aufweisen wie die tatsächlichen Sammlungstypen. Daher ist es möglich, den serialisierten Typ als einen Sammlungsschnittstellentyp zu deklarieren. Die Ergebnisse sind die gleichen wie bei der Verwendung eines tatsächlichen Sammlungstyps. So stimmen beispielsweise die folgenden Datenverträge überein:

[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

Wenn der deklarierte Typ eine Schnittstelle ist, kann der derzeit verwendete Instanzentyp während der Serialisierung ein beliebiger Typ sein, der diese Schnittstelle implementiert. Die oben erläuterten Einschränkungen (das Verfügen über einen Standardkonstruktor und eine Add-Methode) gelten nicht. Beispielsweise können Sie Adressen in Customer2 auf eine Instanz einer generischen ReadOnlyCollection<T> von "Adress" einstellen, auch wenn es Ihnen nicht möglich ist, einen Datenmember des Typs "Generische ReadOnlyCollection<T>" direkt zu deklarieren.

Wenn der deklarierte Typ eine Schnittstelle ist, wählt das Serialisierungsmodul während der Deserialisierung einen Typ, der die deklarierte Schnittstelle implementiert, und der Typ wird instanziiert. Der Mechanismus der bekannten Typen (wie unter Bekannte Typen in Datenverträgen beschrieben) ist hier nicht wirksam; die Typwahl ist in WCF integriert.

Anpassen von Sammlungstypen

Sie können Sammlungstypen anpassen, indem Sie das CollectionDataContractAttribute-Attribut verwenden, das auf verschiedene Weise genutzt werden kann.

Beachten Sie, dass das Anpassen von Sammlungstypen die Austauschbarkeit von Sammlungen beeinträchtigt. Daher wird empfohlen, das Anwenden dieses Attributs wenn möglich zu vermeiden.Weitere Informationen finden Sie unter zu diesem Problem finden Sie weiter unten in diesem Thema im Abschnitt "Erweiterte Sammlungsregeln".

Benennen von Sammlungsdatenverträgen

Die Regeln für das Benennen von Sammlungstypen ähneln den Regeln für das Benennen von Datenvertragstypen, wie unter Datenvertragsnamen beschrieben. Es bestehen jedoch einige wichtige Unterschiede:

  • Zum Anpassen des Namens wird das CollectionDataContractAttribute-Attribut verwendet und nicht das DataContractAttribute-Attribut. Das CollectionDataContractAttribute-Attribut weist außerdem Name- und Namespace-Eigenschaften auf.

  • Wenn das CollectionDataContractAttribute-Attribut nicht angewendet wird, hängen der Standardname und der Standardnamespace für Sammlungstypen von den Namen und Namespaces der Typen ab, die in der Sammlung enthalten sind. Sie werden nicht vom Namen und dem Namespace des Sammlungstyps selbst beeinflusst. Ein Beispiel finden Sie in den folgenden Typen:

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

Die Datenvertragsnamen beider Typen lauten "ArrayOfstring" (und nicht "CustomerList1" oder "StringList1"), d. h., dass sich durch die Serialisierung eines dieser Typen auf der Stammebene XML-Code ähnlich dem folgenden Code ergibt:

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

Diese Benennungsregel wurde gewählt, um sicherzustellen, dass sämtliche nicht benutzerdefinierten Typen, die eine Liste von Zeichenfolgen darstellen, den gleichen Datenvertrag und die gleiche XML-Darstellung haben. Dies macht Sammlungsaustauschbarkeit möglich. In diesem Beispiel sind "CustomerList1" und "StringList1" vollständig austauschbar.

Wenn jedoch das CollectionDataContractAttribute-Attribut angewendet wird, wird die Sammlung zu einem benutzerdefinierten Sammlungsdatenvertrag, auch wenn keine Eigenschaften für das Attribut festgelegt wurden. Der Name und der Namespace des Sammlungsdatenvertrags hängen dann vom Sammlungstyp selbst ab. Ein Beispiel finden Sie im folgenden Typ:

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

Nach der Serialisierung ähneln die resultierenden XML-Daten Folgendem:

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

Beachten Sie, dass dies nicht mehr der XML-Darstellung der nicht benutzerdefinierten Typen entspricht.

  • Sie können die Name- und die Namespace-Eigenschaft verwenden, um die Benennung noch weiter anzupassen. Siehe folgende Klasse.

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

Die resultierenden XML-Daten ähneln Folgendem:

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

Weitere Informationen finden Sie unter dem Abschnitt "Erweiterte Sammlungsregeln" weiter unten in diesem Thema.

Anpassen des sich wiederholenden Elementnamens in Listensammlungen

Listensammlungen enthalten sich wiederholende Einträge. In der Regel wird jeder sich wiederholende Eintrag als Element dargestellt, das gemäß dem Datenvertragsnamen des in der Sammlung enthaltenen Typs benannt ist.

In den CustomerList-Beispielen enthielten die Sammlungen Zeichenfolgen. Der Datenvertragsname des primitiven Zeichenfolgentyps lautet "string", daher war das sich wiederholende Element "<string>".

Wenn jedoch die ItemName-Eigenschaft auf dem CollectionDataContractAttribute-Attribut verwendet wird, kann dieser sich wiederholende Elementname angepasst werden. Ein Beispiel finden Sie im folgenden Typ:

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

Die resultierenden XML-Daten ähneln Folgendem:

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

Der Namespace des sich wiederholenden Elements entspricht stets dem Namespace des Sammlungsdatenvertrags, der mithilfe der Namespace-Eigenschaft angepasst werden kann, wie zuvor beschrieben.

Anpassen von Wörterbuchsammlungen

Wörterbuchsammlungen sind im Grunde Listen von Einträgen, bei denen jeder Eintrag über einen Schlüssel gefolgt von einem Wert verfügt. Genau wie bei normalen Listen können Sie den dem sich wiederholenden Element entsprechenden Elementnamen mit der ItemName-Eigenschaft ändern.

Darüber hinaus können Sie die Elementnamen ändern, die für den Schlüssel und den Wert stehen, indem Sie die KeyName- und die ValueName-Eigenschaft verwenden. Die Namespaces für diese Elemente entsprechen dem Namespace des Sammlungsdatenvertrags.

Ein Beispiel finden Sie im folgenden Typ:

[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

Nach der Serialisierung ähneln die resultierenden XML-Daten Folgendem:

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

Weitere Informationen finden Sie unter zu Wörterbuchsammlungen finden Sie weiter unten in diesem Thema im Abschnitt "Erweiterte Sammlungsregeln".

Sammlungen und bekannte Typen

Sie müssen Sammlungstypen nicht bekannten Typen hinzufügen, wenn sie polymorph anstelle von anderen Sammlungen oder Sammlungsschnittstellen verwendet werden. Wenn Sie beispielsweise einen Datenmember des Typs IEnumerable deklarieren und dazu verwenden, eine Instanz von ArrayList zu senden, müssen Sie ArrayList nicht bekannten Typen hinzufügen.

Wenn Sie anstelle von Typen polymorphe Sammlungen verwenden, bei denen es sich nicht um Sammlungstypen handelt, müssen sie bekannten Typen hinzugefügt werden. Wenn Sie beispielsweise einen Datenmember des Typs Object deklarieren und dazu verwenden, eine Instanz von ArrayList zu senden, müssen Sie ArrayList bekannten Typen hinzufügen.

Dies ermöglicht es Ihnen nicht, eine äquivalente Sammlung polymorph zu serialisieren. Wenn Sie beispielsweise ArrayList der Liste bekannter Typen im vorherigen Beispiel hinzufügen, ist es Ihnen nicht möglich, die Array of Object-Klasse zuzuweisen, obwohl sie den gleichen Datenvertrag aufweist. Dieses Verhalten unterscheidet sich nicht vom regulären Verhalten bekannter Typen bei der Serialisierung für Typen, bei denen es sich nicht um Sammlungstypen handelt. Es ist jedoch besonders wichtig, sich im Falle von Sammlungen hierüber im Klaren zu sein, da Sammlungen sehr häufig äquivalent sind.

Während der Serialisierung kann lediglich ein Typ in einem bestimmten Bereich für einen bestimmten Datenvertrag bekannt sein, und äquivalente Sammlungen besitzen alle die gleichen Datenverträge. Das bedeutet, dass Sie im vorherigen Beispiel nicht beide Elemente (ArrayList und Array of Object) bekannten Typen im gleichen Bereich hinzufügen können. Dies entspricht erneut dem Verhalten bekannter Typen für Typen, bei denen es sich nicht um Sammlungstypen handelt. Es ist jedoch besonders wichtig, sich im Falle von Sammlungen hierüber im Klaren zu sein.

Bekannte Typen sind möglicherweise auch für Inhalte von Sammlungen erforderlich. Wenn beispielsweise eine ArrayList wirklich Instanzen eines Type1 und eines Type2 enthält, sollten beide Typen bekannten Typen hinzugefügt werden.

Im folgenden Beispiel wird ein ordnungsgemäß erstelltes Objektdiagramm mit Sammlungen und bekannten Typen dargestellt. (Das Beispiel ist frei erfunden, da Sie in einer echten Anwendung normalerweise die folgenden Datenmember nicht als Object definieren und daher auch keine Probleme mit bekannten Typen\/Polymorphie auftreten würden.)

[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

Wenn der deklarierte Typ ein Sammlungstyp ist, wird der deklarierte Typ bei der Deserialisierung instanziiert, unabhängig vom Typ, der tatsächlich gesendet wurde. Wenn der deklarierte Typ eine Sammlungsschnittstelle ist, wählt das Deserialisierungsprogramm einen zu instanziierenden Typ aus, unabhängig von bekannten Typen.

Wenn der deklarierte Typ bei der Deserialisierung kein Sammlungstyp ist, jedoch ein Sammlungstyp gesendet wird, wird ein entsprechender Sammlungstyp aus der Liste der bekannten Typen ausgewählt. Es ist möglich, bei der Deserialisierung der Liste bekannter Typen Sammlungsschnittstellentypen hinzuzufügen. In diesem Fall wählt das Deserialisierungsmodul erneut einen Typ aus, der instanziiert werden soll.

Sammlungen und die NetDataContractSerializer-Klasse

Wenn die NetDataContractSerializer-Klasse verwendet wird, verlieren nicht benutzerdefinierte Sammlungstypen (ohne das CollectionDataContractAttribute-Attribut), bei denen es sich nicht um Arrays handelt, ihre spezielle Bedeutung.

Nicht benutzerdefinierte Sammlungstypen, die mit dem SerializableAttribute-Attribut gekennzeichnet sind, können dennoch von der NetDataContractSerializer-Klasse gemäß den Regeln für das SerializableAttribute-Attribut oder die ISerializable-Schnittstelle serialisiert werden.

Benutzerdefinierte Sammlungstypen, Sammlungsschnittstellen und Arrays werden weiterhin als Sammlungen behandelt, selbst wenn die NetDataContractSerializer-Klasse verwendet wird.

Sammlungen und Schema

Alle äquivalenten Sammlungen verfügen über die gleiche Darstellung im XSD-Schema (XML Schema Definition Language). Daher erhalten Sie im generierten Clientcode normalerweise nicht den gleichen Sammlungstyp wie auf dem Server. Beispielsweise kann der Server einen Datenvertrag mit einer generischen List<T> für den Datenmember "Integer" verwenden, im generierten Clientcode kann der gleiche Datenmember jedoch ein Array von ganzen Zahlen werden.

Wörterbuchsammlungen sind mit einer WCF-spezifischen Schemaanmerkung gekennzeichnet, die die Sammlungen als Wörterbücher ausweist; andernfalls könnte man sie von Listen, die Einträge mit einem Schlüssel und einem Wert enthalten, nicht unterscheiden. Eine genaue Beschreibung, wie Sammlungen in einem Datenvertragsschema dargestellt werden, finden Sie unter Datenvertrags-Schemareferenz.

Standardmäßig werden Typen nicht für nicht benutzerdefinierte Sammlungen in importiertem Code generiert. Datenmember von Listensammlungstypen werden als Arrays importiert, und Datenmember von Wörterbuchsammlungstypen werden als generisches Wörterbuch importiert.

Für benutzerdefinierte Sammlungen werden jedoch separate Typen generiert, die mit dem CollectionDataContractAttribute-Attribut gekennzeichnet sind. (Ein benutzerdefinierter Sammlungstyp im Schema ist ein Typ, der keinen standardmäßigen Namespace und keine standardmäßigen Namen, sich wiederholende Elementnamen oder Namen für Schlüssel-\/Wertelemente verwendet.) Bei diesen Typen handelt es sich um leere Typen, die von der generischen List<T> für Listentypen und vom generischen Wörterbuch für Wörterbuchtypen abgeleitet werden.

Es können z. B. folgende Typen auf dem Server vorliegen:

[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

Wenn das Schema exportiert und anschließend wieder importiert wird, ähnelt der generierte Clientcode dem folgenden Code (statt Eigenschaften werden zur Erleichterung des Lesens Felder angezeigt).

[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

Möglicherweise möchten Sie andere Typen als die Standardtypen in generiertem Code verwenden. Es könnte z. B. sein, dass Sie die generische BindingList<T> statt regulärer Arrays für Ihre Datenmember verwenden möchten, um sie leichter an Benutzerschnittstellenkomponenten binden zu können.

Übergeben Sie zum Auswählen der zu generierenden Sammlungstypen eine Liste der Sammlungstypen, die Sie verwenden möchten, in die ReferencedCollectionTypes -Eigenschaft des ImportOptions-Objekts, wenn Sie das Schema importieren. Diese Typen werden als referenzierte Sammlungstypen bezeichnet.

Wenn auf generische Typen verwiesen wird, müssen sie entweder vollständig offene oder vollständig geschlossene Generika sein.

Hinweis

Wenn Sie das Tool „Svcutil.exe“ verwenden, kann dieser Verweis über den \/collectionType-Befehlszeilenschalter (Kurzform: \/ct) erreicht werden. Denken Sie daran, dass Sie auch die Assembly für die referenzierten Sammlungstypen über den \/reference-Schalter (Kurzform: \/r) angeben müssen. Bei generischen Typen folgt auf den Namen des Typs ein Graviszeichen und die Anzahl der generischen Parameter. Verwechseln Sie das Graviszeichen (`) nicht mit dem einfachen Anführungszeichen (‘). Sie können mehrere referenzierte Sammlungstypen angeben, indem Sie den \/collectionType-Schalter mehrmals verwenden.

Beispielsweise, um zu bewirken, dass alle Listen als generische List<T> importiert werden.

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

Beim Importieren einer beliebigen Sammlung wird die Liste der referenzierten Sammlungstypen gescannt, und die am besten passende Sammlung wird verwendet (sofern vorhanden) – entweder als Datenmembertyp (für nicht benutzerdefinierte Sammlungen) oder als Basistyp, von dem abgeleitet wird (für benutzerdefinierte Sammlungen). Wörterbücher werden nur Wörterbüchern gegenübergestellt, während Listen Listen gegenübergestellt werden.

Wenn Sie beispielsweise die generische BindingList<T> und Hashtable der Liste referenzierter Typen hinzufügen, ähnelt der generierte Clientcode für das vorangegangene Beispiel dem folgenden.

[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

Sie können Sammlungsschnittstellentypen als Teil Ihrer referenzierten Sammlungstypen angeben, jedoch können Sie keine ungültigen Sammlungstypen angeben (wie diejenigen ohne Add-Methode oder öffentlichen Konstruktor).

Ein geschlossener generischer Typ wird als beste Übereinstimmung betrachtet. (Nicht generische Typen werden als äquivalent zu geschlossenen Generika von Object betrachtet). Wenn beispielsweise die Typen "Generische List<T> von DateTime", "Generische BindingList<T>" (offener generischer Typ) und "ArrayList" die referenzierten Sammlungstypen sind, wird Folgendes generiert:

[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

Für Listensammlungen werden nur die Fälle aus der folgenden Tabelle unterstützt:

Referenzierter Typ Vom referenzierten Typ implementierte Schnittstelle Beispiel Typ behandelt als:
Nicht generisch oder geschlossen generisch (beliebige Anzahl von Parametern) Nicht generisch MyType : IList

oder

MyType<T> : IList

, wobei T= int
Geschlossen generisch von Object (z. B. IList<object>)
Nicht generisch oder geschlossen generisch (beliebige Anzahl von Parametern, die nicht notwendigerweise mit dem Sammlungstyp übereinstimmen) Geschlossen generisch MyType : IList<string>

oder

MyType<T> : IList<string>, wobei T=int
Geschlossen generisch (z. B. IList<string>)
Geschlossen generisch mit einer beliebigen Anzahl an Parametern Offen generisch mit einem beliebigen Parameter des Typs MyType<T,U,V> : IList<U>

, wobei T=int, U=string, V=bool
Geschlossen generisch (z. B. IList<string>)
Offen generisch mit einem Parameter Offen generisch mit dem Parameter des Typs MyType<T> : IList<T>, T ist offen Offen generisch (z. B. IList<T>)

Wenn ein Typ mehr als eine Listensammlungsschnittstelle implementiert, gelten die folgenden Einschränkungen:

  • Wenn der Typ das generische IEnumerable<T> (oder die davon abgeleiteten Schnittstellen) mehrfach für verschiedene Typen implementiert, gilt der Typ nicht als gültiger referenzierter Sammlungstyp und wird ignoriert. Dies gilt auch, wenn einige Implementierungen ungültig sind oder offene Generika verwenden. Beispielsweise würde ein Typ, der das generische IEnumerable<T> von int und das generische IEnumerable<T> von T implementiert, niemals als referenzierte Sammlung von int oder eines anderen Typs verwendet werden, unabhängig davon, ob der Typ eine Add-Methode, die int akezptiert, oder eine Add-Methode, die einen Parameter vom Typ T akzeptiert, oder beides aufweist.

  • Wenn der Typ eine generische Sammlungsschnittstelle sowie IList implementiert, wird er niemals als referenzierter Sammlungstyp verwendet, es sei denn, die generische Sammlungsschnittstelle ist ein geschlossener generischer Typ vom Typ Object.

Für Wörterbuchsammlungen werden nur die Fälle aus der folgenden Tabelle unterstützt:

Referenzierter Typ Vom referenzierten Typ implementierte Schnittstelle Beispiel Typ behandelt als
Nicht generisch oder geschlossen generisch (beliebige Anzahl von Parametern) IDictionary MyType : IDictionary

oder

MyType<T> : IDictionary, wobei T=int
Geschlossenes generisches IDictionary<object,object>
Geschlossen generisch (beliebigen Anzahl an Parametern) IDictionary<TKey,TValue>, geschlossen MyType<T> : IDictionary\<string, bool>, wobei T=int Geschlossen generisch (z. B. IDIctionary\<string,bool>)
Geschlossen generisch (beliebigen Anzahl an Parametern) Generisches IDictionary<TKey,TValue>, entweder der Schlüssel oder der Wert ist geschlossen, das andere Element ist offen und verwendet einen Parameter des Typs MyType\<T,U,V> : IDictionary\<string,V>, wobei T=int, U=float, V=bool

oder

MyType<Z> : IDictionary\<Z,bool>, wobei Z=string
Geschlossen generisch (z. B. IDictionary\<string,bool>)
Geschlossen generisch (beliebigen Anzahl an Parametern) Generisches IDictionary<TKey,TValue>, sowohl Schlüssel als auch Wert sind geöffnet und verwenden beide jeweils einen Parameter des Typs MyType\<T,U,V> : IDictionary\<V,U>, wobei T=int, U=bool, V=string Geschlossen generisch (z. B. IDictionary\<string,bool>)
Offen generisch (zwei Parameter) Generisches IDictionary<TKey,TValue>, offen, verwendet beide generischen Parametern des Typs in der Reihenfolge, in der sie angezeigt werden. MyType\<K,V> : IDictionary\<K,V>, K und V beide offen Offen generisch (z. B. IDictionary\<K,V>)

Wenn der Typ sowohl das IDictionary als auch das generische IDictionary<TKey,TValue> implementiert, wird nur das generische IDictionary<TKey,TValue> betrachtet.

Das Verweisen auf teilweise generische Typen wird nicht unterstützt.

Duplikate sind nicht zulässig. Beispielsweise können Sie nicht sowohl die generische List<T> von Integer als auch die generische Sammlung von IntegerReferencedCollectionTypes hinzufügen, da anschließend nicht mehr festgestellt werden kann, was verwendet werden soll, wenn eine Liste mit ganzen Zahlen im Schema auftritt. Duplikate werden nur erkannt, wenn es einen Typ im Schema gibt, der das Duplikatproblem offenlegt. Wenn z. B. das importierte Schema keine Liste mit ganzen Zahlen enthält, kann es sowohl über die generische List<T> von Integer als auch über die generische Sammlung von Integer in den ReferencedCollectionTypes verfügen, doch nichts davon zeigt eine Wirkung.

Erweiterte Sammlungsregeln

Serialisieren von Sammlungen

Im Folgenden finden Sie eine Liste mit Sammlungsregeln für die Serialisierung:

  • Das Kombinieren von Sammlungstypen (das Erstellen von Sammlungen von Sammlungen) ist zulässig. Verzweigte Arrays werden als Sammlungen von Sammlungen behandelt. Arrays mit mehreren Dimensionen werden nicht unterstützt.

  • Byte- und XmlNode-Arrays sind besondere Arraytypen, die als Primitive und nicht als Sammlungen behandelt werden. Die Serialisierung eines Bytearrays führt zu einem einzelnen XML-Element, das statt eines separaten Elements für jedes Byte einen Abschnitt aus Base64-codierten Daten enthält.Weitere Informationen finden Sie unter zur Behandlung eines Arrays von XmlNode finden Sie unter XML- und ADO.NET-Typen in Datenverträgen. Natürlich können diese besonderen Typen selbst Teil von Sammlungen sein: Ein Bytearray führt zu mehreren XML-Elementen, die jeweils einen Abschnitt aus Base64-codierten Daten enthalten.

  • Wenn das DataContractAttribute-Attribut auf einen Sammlungstyp angewendet wird, wird der Typ als regulärer Datenvertragstyp und nicht als Sammlung behandelt.

  • Wenn ein Sammlungstyp die IXmlSerializable-Schnittstelle implementiert, dann gelten die folgenden Regeln, wenn ein myType:IList<string>, IXmlSerializable-Typ vorliegt:

    • Wird als Typ IList<string> deklariert, wird der Typ als Liste serialisiert.

    • Wird als Typ myType deklariert, wird der Typ als IXmlSerializable serialisiert.

    • Wenn der deklarierte Typ IXmlSerializable lautet, wird der Typ nur dann als IXmlSerializable serialisiert, wenn Sie myType der Liste bekannter Typen hinzufügen.

  • Sammlungen werden mit den Methoden aus der folgenden Tabelle serialisiert und deserialisiert:

Sammlungstyp implementiert Bei der Serialisierung aufgerufene Methode(n) Bei der Deserialisierung aufgerufene Methode(n)
Generisches IDictionary<TKey,TValue> get_Keys, get_Values Generisches Hinzufügen
IDictionary get_Keys, get_Values Add
Generisches IList<T> Generischer IList<T>-Indexer Generisches Hinzufügen
Generisches ICollection<T> Enumerator Generisches Hinzufügen
IList IList-Indexer Add
Generisches IEnumerable<T> GetEnumerator Eine nicht statische Methode mit dem Namen Add, die einen Parameter des entsprechenden Typs annimmt (den Typ des generischen Parameters oder einen der Basistypen). Eine solche Methode ist erforderlich, damit das Serialisierungsprogramm einen Sammlungstyp sowohl während der Serialisierung als auch während der Deserialisierung als Sammlung behandelt.
IEnumerable (und daher ICollection, die sich daraus ableitet) GetEnumerator Eine nicht statische Methode namens Add, die einen Parameter des Typs Object annimmt. Eine solche Methode ist erforderlich, damit das Serialisierungsprogramm einen Sammlungstyp sowohl während der Serialisierung als auch während der Deserialisierung als Sammlung behandelt.

In der vorangehenden Tabelle werden in absteigender Reihenfolge Sammlungsschnittstellen aufgelistet. Das bedeutet beispielsweise, dass die Sammlung gemäß den IList-Regeln serialisiert und deserialisiert wird, wenn ein Typ sowohl die IEnumerable<T> als auch das generische IList implementiert:

  • Bei der Deserialisierung werden alle Sammlungen deserialisiert, indem zunächst durch Aufrufen des Standardkonstruktors eine Instanz des Typs erstellt wird. Der Standardkonstruktor ist erforderlich, damit das Serialisierungsprogramm einen Sammlungstyp sowohl während der Serialisierung als auch während der Deserialisierung als Sammlung behandelt.

  • Wenn die gleiche generische Sammlungsschnittstelle mehrmals implementiert wird (z. B. wenn ein Typ sowohl die generische ICollection<T> von Integer als auch die generische ICollection<T> von String implementiert) und keine Schnittstelle mit höherer Rangfolge gefunden wird, wird die Sammlung nicht als gültige Sammlung behandelt.

  • Auf Sammlungstypen kann das SerializableAttribute-Attribut angewendet sein, und sie können die ISerializable-Schnittstelle implementieren. Beide werden ignoriert. Wenn jedoch der Typ die Sammlungstypanforderungen nicht vollständig erfüllt (z. B. wenn der Sammlungstyp und die Add-Methode fehlen), wird der Typ nicht als Sammlungstyp betrachtet. Daher werden das SerializableAttribute-Attribut und die ISerializable-Schnittstelle verwendet, um die Serialisierbarkeit des Typs zu bestimmen.

  • Durch Anwenden des CollectionDataContractAttribute-Attributs auf eine Sammlung, um diese anzupassen, wird der oben genannte SerializableAttribute-Fallback-Mechanismus entfernt. Stattdessen wird eine InvalidDataContractException-Ausnahme ausgelöst, wenn eine benutzerdefinierte Sammlung keine Sammlungstypanforderungen erfüllt,. Die Ausnahmezeichenfolge enthält oftmals Informationen, mit denen erläutert wird, warum ein angegebener Typ nicht als gültige Sammlung betrachtet wird (keine Add-Methode, kein Standardkonstruktor usw.), weshalb es oft hilfreich sein kann, das CollectionDataContractAttribute-Attribut zum Zwecke des Debuggings anzuwenden.

Sammlungsbenennung

Im Folgenden finden Sie eine Liste von Regeln für die Sammlungsbenennung:

  • Der Standardnamespace für alle Datenverträge für Wörterbuchsammlungen sowie für Datenverträge für Listensammlungen, die primitive Typen enthalten, lautet http:\/\/schemas.microsoft.com\/2003\/10\/Serialization\/Arrays, es sei denn, er wurde von "Namespace" überschrieben. Typen, die integrierten XSD-Typen zugeordnet werden, sowie char-, Timespan- und Guid-Typen, gelten zu diesem Zweck als primitive Typen.

  • Der Standardnamespace für Sammlungstypen, die nicht primitive Typen enthalten, ist der gleiche wie der Namespace für die Datenverträge des in der Sammlung enthaltenen Typs, sofern er nicht von "Namespace" überschrieben wurde.

  • Der Standardname für Datenverträge für Sammlungstypen ist die Zeichenfolge "ArrayOf" in Kombination mit dem Datenvertragsnamen des in der Sammlung enthaltenen Typs, sofern er nicht von "Name" überschrieben wurde. Der Datenvertragsname einer generischen Liste von ganzen Zahlen lautet z. B. "ArrayOfint". Beachten Sie, dass der Datenvertragsname von Object "anyType" lautet, sodass der Datenvertragsname für nicht generische Listen wie ArrayList "ArrayOfanyType" lautet.

Der Standardname für Datenverträge für Wörterbuchsammlungen ist die Zeichenfolge "ArrayOfKeyValueOf" in Kombination mit dem Datenvertragsnamen für den Schlüsseltyp, gefolgt vom Datenvertragsnamen des Werttyps, sofern er nicht von Name überschrieben wurde. Der Datenvertragsname für ein generisches Wörterbuch für Zeichenfolge und ganze Zahl lautet z. B. "ArrayOfKeyValueOfstringint". Wenn darüber hinaus der Schlüssel- oder der Werttyp kein primitiver Typ ist, wird an den Namen ein Namespacehash der Datenvertragsnamespaces für den Schlüssel- und Werttyp angefügt.Weitere Informationen finden Sie unter zu Namespacehashes finden Sie unter Datenvertragsnamen.

Jeder Wörterbuchsammlungsdatenvertrag weist einen Begleitdatenvertrag auf, der einen Eintrag im Wörterbuch darstellt. Der Name ist der gleiche wie der des Wörterbuchdatenvertrags, bis auf das Präfix "ArrayOf", und der Namespace ist ebenfalls der gleiche wie für den Wörterbuchdatenvertrag. Beispielsweise kann für den "ArrayOfKeyValueOfstringint"-Wörterbuchdatenvertrag der "KeyValueofstringint"-Datenvertrag einen Eintrag im Wörterbuch darstellen. Sie können den Namen dieses Datenvertrags individuell anpassen, indem Sie die ItemName-Eigenschaft verwenden, wie im nächsten Abschnitt beschrieben.

Benennungsregeln für generische Typen, wie unter Datenvertragsnamen beschrieben, gelten vollständig für Sammlungstypen, d. h. Sie können geschweifte Klammern innerhalb von "Name" verwenden, um generische Parametertypen anzuzeigen. Die Zahlen innerhalb der Klammern verweisen jedoch auf generische Parameter und nicht auf Typen, die in der Sammlung enthalten sind.

Sammlungsanpassung

Die folgenden Verwendungen des CollectionDataContractAttribute-Attributs sind unzulässig und führen zu einer InvalidDataContractException-Ausnahme:

Polymorphieregeln

Wie bereits erwähnt, behindert das Anpassen von Sammlungen mit dem CollectionDataContractAttribute-Attribut möglicherweise die Austauschbarkeit der Sammlungen. Zwei benutzerdefinierte Sammlungstypen gelten nur als gleich, wenn ihre Namen, ihr Namespace, ihr Elementname und ihre Schlüssel- und Wertnamen (sofern es sich um Wörterbuchsammlungen handelt) übereinstimmen.

Aufgrund der Anpassungen ist es möglich, einen Sammlungsdatenvertrag, wo noch ein weiterer erwartet wird, versehentlich zu verwenden. Dies sollte vermieden werden. Siehe folgende Typen:

[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

In diesem Fall kann eine Instanz von Marks1``testMarks zugewiesen werden.Marks2 sollte jedoch nicht verwendet werden: Der zugehörige Datenvertrag stimmt nicht mit dem IList<int>-Datenvertrag überein. Der Datenvertragsname lautet "Marks2" und nicht "ArrayOfint", und der sich wiederholende Elementname lautet "<mark>" und nicht "<int>".

Die Regeln in der folgenden Tabelle gelten für die polymorphe Zuweisung von Sammlungen:

Deklarierter Typ Zuweisen einer nicht benutzerdefinierten Sammlung Zuweisen einer benutzerdefinierten Sammlung
Objekt Vertragsname wird serialisiert. Vertragsname wird serialisiert.

Anpassung wird verwendet.
Sammlungsschnittstelle Vertragsname wird nicht serialisiert. Vertragsname wird nicht serialisiert.

Anpassung wird nicht verwendet.*
Nicht benutzerdefinierte Sammlung Vertragsname wird nicht serialisiert. Vertragsname wird serialisiert.

Anpassung wird verwendet.**
Benutzerdefinierte Sammlung Vertragsname wird serialisiert. Anpassung wird nicht verwendet.** Vertragsname wird serialisiert.

Anpassung des zugewiesenen Typs wird verwendet. **

* Mit der NetDataContractSerializer-Klasse wird Anpassung in diesem Fall verwendet. Die NetDataContractSerializer-Klasse serialisiert auch den tatsächlichen Typnamen in diesem Fall, sodass die Deserialisierung wie erwartet funktioniert.

** Diese Fälle führen zu Instanzen, die dem Schema nach ungültig sind und somit vermieden werden sollten.

In Fällen, in denen der Vertragsname serialisiert wird, sollte die Liste der bekannten Typen den zugewiesenen Sammlungstyp enthalten. Auch der entgegengesetzte Fall trifft zu: In Fällen, in denen der Name nicht serialisiert wird, muss der Typ nicht der Liste der bekannten Typen hinzugefügt werden.

Einem Array eines Basistyps kann ein Array eines abgeleiteten Typs zugewiesen werden. In diesem Fall wird der Vertragsname für den abgeleiteten Typ für jedes sich wiederholende Element serialisiert. Wenn z. B. ein Typ Book vom Typ LibraryItem abgeleitet wurde, können Sie ein Array von Book einem Array von LibraryItem hinzufügen. Dies gilt nicht für andere Sammlungstypen. Sie können z. B. eine Generic List of Book nicht einem Generic List of LibraryItem zuweisen. Sie können aber ein Generic List of LibraryItem zuweisen, das Book-Instanzen enthält. Sowohl im Fall mit Array als auch im Fall ohne Array sollte Book in der Liste der bekannten Typen enthalten sein.

Sammlungen und Beibehaltung von Objektverweisen

Wenn ein Serialisierungsprogramm in einem Modus ausgeführt wird, in dem es Objektverweise beibehält, gilt die Beibehaltung der Objektverweise auch für Sammlungen. Insbesondere wird Objektidentität sowohl für ganze Sammlungen als auch für einzelne Elemente beibehalten, die in Sammlungen enthalten sind. Für Wörterbücher wird Objektidentität sowohl für Schlüssel- und Wertepaarobjekte als auch für die einzelnen Schlüssel- und Werteobjekte beibehalten.

Siehe auch

CollectionDataContractAttribute