Teilen über


Sammlungstypen in Datenverträgen

Eine Auflistung ist eine Liste von Elementen eines bestimmten Typs. In .NET Framework können solche Listen mithilfe von Arrays oder einer Vielzahl anderer Typen dargestellt werden (Generic List, Generic BindingList<T>, StringCollectionoder ArrayList). Beispielsweise kann eine Sammlung eine Liste der Adressen für einen bestimmten Kunden enthalten. Diese Auflistungen werden unabhängig vom tatsächlichen Typ als Listensammlungen bezeichnet.

Eine spezielle Form der Auflistung ist vorhanden, die eine Zuordnung zwischen einem Element (dem Schlüssel) und einem anderen (dem "Wert") darstellt. In .NET Framework werden diese durch Typen wie Hashtable z. B. und das generische Wörterbuch dargestellt. Eine Zuordnungssammlung kann z. B. eine Stadt ("Schlüssel") der zugehörigen Bevölkerung ("Wert") zuordnen. Diese Auflistungen werden unabhängig vom tatsächlichen Typ als Wörterbuchsammlungen bezeichnet.

Sammlungen erhalten besondere Behandlung im Datenvertragsmodell.

Typen, die die IEnumerable Schnittstelle implementieren, einschließlich Arrays und generischen Auflistungen, werden als Sammlungen erkannt. Von diesen Typen, die die IDictionary Oder generische IDictionary<TKey,TValue> Schnittstellen implementieren, sind Wörterbuchsammlungen; alle anderen sind Listensammlungen.

Weitere Anforderungen an Sammlungstypen, z. B. das Aufrufen einer Methode Add und ein parameterloser Konstruktor, werden in den folgenden Abschnitten ausführlich erläutert. Dadurch wird sichergestellt, dass Sammlungstypen sowohl serialisiert als auch deserialisiert werden können. Dies bedeutet, dass einige Auflistungen nicht direkt unterstützt werden, z. B. "Generic ReadOnlyCollection<T> " (da sie keinen parameterlosen Konstruktor aufweist). Informationen zur Umgehung dieser Einschränkungen finden Sie im Abschnitt "Verwenden von Sammlungsschnittstellentypen und Read-Only Sammlungen" weiter unten in diesem Thema.

Die in Sammlungen enthaltenen Typen müssen Datenvertragstypen sein oder anderweitig serialisierbar sein. Weitere Informationen finden Sie unter Typen, die vom Serializer für den Datenvertrag unterstützt werden.

Weitere Informationen dazu, was ist und was nicht als gültige Sammlung betrachtet wird, sowie darüber, wie Sammlungen serialisiert werden, finden Sie in den Informationen zum Serialisieren von Sammlungen im Abschnitt "Erweiterte Sammlungsregeln" dieses Themas.

Austauschbare Sammlungen

Alle Listensammlungen des gleichen Typs gelten als denselben Datenvertrag zu haben (es sei denn, sie werden mithilfe des CollectionDataContractAttribute-Attributes angepasst, wie später in diesem Thema erläutert). So sind beispielsweise die folgenden Datenverträge gleichwertig.

[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 ähnlich dem folgenden Code.

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

Mithilfe der Sammlungstauschbarkeit können Sie z. B. einen sammlungstyp verwenden, der für die Leistung auf dem Server optimiert ist, und einen Sammlungstyp, der an Benutzeroberflächenkomponenten auf dem Client gebunden werden soll.

Ähnlich wie bei Listensammlungen werden alle Wörterbuchsammlungen mit demselben Schlüssel- und Werttyp als derselbe Datenkontrakt betrachtet, es sei denn, sie werden durch das CollectionDataContractAttribute-Attribut angepasst.

Es geht nur um den Datentyp des Datenvertrags, soweit es um die Sammlungsäquivalente geht, nicht um .NET-Typen. Das heißt, eine Sammlung von Type1 wird als Äquivalent zu einer Sammlung von Type2 betrachtet, wenn Type1 und Type2 über gleichwertige Datenverträge verfügen.

Nicht generische Auflistungen gelten als derselbe Datenvertrag wie generische Auflistungen vom Typ Object. (Beispielsweise sind die Datenverträge für ArrayList und Generic List<T> von Object identisch.)

Verwenden von Sammlungsschnittstellentypen und schreibgeschützten Sammlungen

Sammlungsschnittstellentypen (IEnumerable, IDictionarygenerische IDictionary<TKey,TValue>oder von diesen Schnittstellen abgeleitete Schnittstellen) werden auch als Sammlungsdatenverträge betrachtet, die den Sammlungsdatenverträgen für tatsächliche Sammlungstypen entsprechen. Daher ist es möglich, den Typ zu deklarieren, der als Sammlungsschnittstellentyp serialisiert wird, und die Ergebnisse sind identisch mit der Verwendung eines tatsächlichen Sammlungstyps. Beispielsweise sind die folgenden Datenverträge gleichwertig.

[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 zuvor beschriebenen Einschränkungen (mit einem parameterlosen Konstruktor und einer Add Methode) gelten nicht. Sie können z. B. Adressen in Customer2 auf eine Instanz von "Generic ReadOnlyCollection<T> of Address" festlegen, obwohl Sie ein Datenmememm des Typs "Generic" ReadOnlyCollection<T>nicht direkt deklarieren können.

Wenn der deklarierte Typ eine Schnittstelle ist, wählt das Serialisierungsmodul während der Deserialisierung einen Typ aus, der die deklarierte Schnittstelle implementiert, und der Typ wird instanziiert. Der bekannte Typenmechanismus (beschrieben in bekannten Datenvertragstypen) hat hier keine Auswirkung; Die Auswahl des Typs ist in WCF integriert.

Anpassen von Sammlungstypen

Sie können Sammlungstypen mithilfe des CollectionDataContractAttribute Attributs anpassen, das mehrere Verwendungen hat.

Beachten Sie, dass das Anpassen von Sammlungstypen die Austauschbarkeit der Sammlung beeinträchtigt, daher empfiehlt es sich im Allgemeinen, das Anwenden dieses Attributs nach Möglichkeit zu vermeiden. Weitere Informationen zu diesem Problem finden Sie im Abschnitt "Erweiterte Sammlungsregeln" weiter unten in diesem Thema.

Benennung des Sammlungsdatenvertrags

Die Regeln für die Benennung von Sammlungstypen ähneln denen für die Benennung regulärer Datentypen, wie unter "Datenvertragsnamen" beschrieben, obwohl einige wichtige Unterschiede bestehen:

  • Das CollectionDataContractAttribute Attribut wird verwendet, um den Namen anstelle des DataContractAttribute Attributs anzupassen. Das CollectionDataContractAttribute-Attribut hat auch Name- und Namespace-Eigenschaften.

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

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

Der Name des Datenvertrags beider Typen lautet "ArrayOfstring" und nicht "CustomerList1" oder "StringList1". Dies bedeutet, dass die Serialisierung eines dieser Typen auf der Stammebene XML ähnlich dem folgenden Code liefert.

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

Diese Benennungsregel wurde ausgewählt, um sicherzustellen, dass alle nicht angepassten Typen, die eine Liste von Zeichenfolgen darstellen, denselben Datenvertrag und die XML-Darstellung aufweisen. Dies ermöglicht die Austauschbarkeit der Sammlung. In diesem Beispiel sind CustomerList1 und StringList1 vollständig austauschbar.

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

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

Bei der Serialisierung ähnelt der resultierende XML-Code folgendem.

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

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

  • Sie können die Benennung durch die Eigenschaften Name und Namespace weiter anpassen. 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
    

Der resultierende XML-Code ähnelt der folgenden.

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

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

Anpassen des sich wiederholenden Elementnamens in Listensammlungen

Listensammlungen enthalten wiederholte Einträge. Normalerweise wird jeder wiederholte Eintrag als ein Element dargestellt, das entsprechend dem Datenvertragsnamen des in der Sammlung enthaltenen Typs benannt ist.

In den CustomerList Beispielen enthielten die Auflistungen Zeichenfolgen. Der Name des Datenvertrags für den Zeichenfolgengrundtyp lautet "string", sodass das wiederholte Element "<string>" lautet.

Mithilfe der ItemName Eigenschaft für das CollectionDataContractAttribute Attribut kann dieser wiederholte Elementname jedoch angepasst werden. Ein Beispiel finden Sie unter dem folgenden Typ.

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

Der resultierende XML-Code ähnelt der folgenden.

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

Der Namespace des wiederholten Elements ist immer identisch mit dem Namespace des Sammlungsdatenvertrags, der mithilfe der Namespace Eigenschaft angepasst werden kann, wie zuvor beschrieben.

Anpassen von Wörterbuchsammlungen

Wörterbuchsammlungen sind im Wesentlichen Listen mit Einträgen, wobei jeder Eintrag über einen Schlüssel gefolgt von einem Wert verfügt. Genau wie bei regulären Listen können Sie den Elementnamen ändern, der dem wiederholten Element entspricht, indem Sie die ItemName Eigenschaft verwenden.

Darüber hinaus können Sie die Elementnamen ändern, die den Schlüssel und den Wert darstellen, indem Sie die KeyName Eigenschaften verwenden ValueName . Die Namespaces für diese Elemente sind identisch mit dem Namespace des Sammlungsdatenvertrags.

Ein Beispiel finden Sie unter dem 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

Bei der Serialisierung ähnelt der resultierende XML-Code folgendem.

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

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

Sammlungen und bekannte Typen

Sie müssen keine Sammlungstypen zu bekannten Typen hinzufügen, wenn sie anstelle anderer Auflistungen oder Sammlungsschnittstellen polymorph verwendet werden. Wenn Sie z. B. ein Datenelement vom Typ IEnumerable deklarieren und verwenden, um eine Instanz von ArrayList zu senden, müssen Sie ArrayList nicht zu bekannten Typen hinzufügen.

Wenn Sie Sammlungen polymorph anstelle von Nicht-Sammlungstypen verwenden, müssen sie bekannten Typen hinzugefügt werden. Wenn Sie z. B. ein Datenmitglied vom Typ Object deklarieren und verwenden, um eine Instanz von ArrayList zu senden, fügen Sie ArrayList zu den bekannten Typen hinzu.

Dies ermöglicht es Ihnen nicht, eine äquivalente Sammlung polymorph zu serialisieren. Wenn Sie beispielsweise der Liste bekannter Typen im vorherigen Beispiel hinzufügen ArrayList , können Sie die Array of Object Klasse nicht zuweisen, auch wenn sie über einen entsprechenden Datenvertrag verfügt. Dies unterscheidet sich nicht vom normalen Verhalten bekannter Typen bei der Serialisierung für Nicht-Sammlungs-Typen, aber es ist besonders wichtig, dies im Fall von Sammlungen zu verstehen, da es sehr häufig vorkommt, dass Sammlungen gleichwertig sind.

Während der Serialisierung kann nur ein Typ in einem bestimmten Bereich für einen bestimmten Datenvertrag bekannt sein, und gleichwertige Sammlungen verfügen alle über dieselben Datenverträge. Dies bedeutet, dass Sie im vorherigen Beispiel ArrayList und Array of Object nicht beide im selben Bereich zu bekannten Typen hinzufügen können. Wiederum entspricht dies dem Verhalten bekannter Typen bei Nicht-Sammlungstypen, ist jedoch besonders wichtig, wenn es um das Verständnis für Sammlungen geht.

Bekannte Typen können auch für Inhalte von Sammlungen erforderlich sein. Wenn eine ArrayList tatsächlich Instanzen von Type1 und Type2 enthält, sollten beide Typen zu den bekannten Typen hinzugefügt werden.

Das folgende Beispiel zeigt ein ordnungsgemäß konstruiertes Objektdiagramm mit Auflistungen und bekannten Typen. (Das Beispiel ist frei erfunden, da Sie in einer echten Anwendung normalerweise die folgenden Datenmember nicht als Objectdefinieren 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

Bei der Deserialisierung wird der deklarierte Typ unabhängig vom tatsächlich gesendeten Typ instanziiert, wenn der deklarierte Typ ein Sammlungstyp ist. Wenn der deklarierte Typ eine Sammlungsschnittstelle ist, wählt der Deserializer einen Typ aus, der ohne Bezug auf bekannte Typen instanziiert werden soll.

Außerdem wird bei der Deserialisierung, wenn der deklarierte Typ kein Sammlungstyp ist, aber ein Sammlungstyp gesendet wird, ein übereinstimmenden Sammlungstyp aus der Liste der bekannten Typen ausgewählt. Es ist möglich, der Liste bekannter Typen bei der Deserialisierung Sammlungsschnittstellentypen hinzuzufügen. In diesem Fall wählt die Deserialisierungs-Engine erneut einen Typ aus, der instanziiert werden soll.

Auflistungen und die NetDataContractSerializer-Klasse

Wenn die NetDataContractSerializer Klasse verwendet wird, verlieren nicht angepasste Auflistungstypen (ohne das CollectionDataContractAttribute Attribut), die keine Arrays sind, ihre besondere Bedeutung.

Nicht angepasste Sammlungstypen, die mit dem SerializableAttribute Attribut gekennzeichnet sind, können weiterhin von der NetDataContractSerializer Klasse gemäß dem SerializableAttribute Attribut oder den ISerializable Schnittstellenregeln serialisiert werden.

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

Sammlungen und Schemata

Alle äquivalenten Auflistungen weisen die gleiche Darstellung im XML-Schema für die Schemadefinitionssprache (XSD) auf. Aus diesem Grund erhalten Sie normalerweise nicht dieselbe Sammlungsart im generierten Clientcode wie den auf dem Server. Der Server kann zum Beispiel einen Datenvertrag mit einem generischen List<T> Integer-Datenmitglied verwenden, aber im generierten Clientcode kann dasselbe Datenelement zu einem Array von Ganzzahlen werden.

Wörterbuchsammlungen werden mit einer WCF-spezifischen Schemaanmerkung gekennzeichnet, die darauf hinweist, dass sie Wörterbücher sind; andernfalls sind sie nicht von einfachen Listen zu unterscheiden, die Einträge mit einem Schlüssel und einem Wert enthalten. Eine genaue Beschreibung der Darstellung von Sammlungen im Datenvertragsschema finden Sie unter Data Contract Schema Reference.

Standardmäßig werden Typen für nicht angepasste Auflistungen im importierten Code nicht generiert. Datenmember von Listensammlungstypen werden als Arrays importiert, und Datenmember von Wörterbuchsammlungstypen werden als generisches Wörterbuch importiert.

Bei benutzerdefinierten Auflistungen werden jedoch separate Typen generiert, die mit dem CollectionDataContractAttribute Attribut gekennzeichnet sind. (Ein benutzerdefinierter Sammlungstyp im Schema ist ein Typ, der den Standardnamespace, den Namen, den wiederholten Elementnamen oder schlüssel/wert-Elementnamen nicht verwendet.) Diese Typen sind leere Typen, die von "Generic" List<T> für Listentypen und "Generic Dictionary" für Wörterbuchtypen abgeleitet werden.

Sie können z. B. die folgenden Typen auf dem Server haben.

[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 erneut exportiert und wieder importiert wird, ähnelt der generierte Clientcode den folgenden (Felder werden anstelle von Eigenschaften zum einfacheren Lesen 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 unterschiedliche Typen im generierten Code als die Standardtypen verwenden. Sie können beispielsweise "Generic" BindingList<T> anstelle regulärer Arrays für Ihre Datenelemente verwenden, um die Bindung an Benutzeroberflächenkomponenten zu vereinfachen.

Um sammlungstypen auszuwählen, die generiert werden sollen, übergeben Sie eine Liste von Auflistungstypen, die Sie beim Importieren des Schemas in die ReferencedCollectionTypes Eigenschaft des ImportOptions Objekts verwenden möchten. Diese Typen werden als referenzierte Sammlungstypen bezeichnet.

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

Hinweis

Bei Verwendung des tools Svcutil.exe kann dieser Verweis mithilfe des Befehlszeilenschalters /collectionType (kurz: /ct) erreicht werden. Denken Sie daran, dass Sie auch die Assembly für die referenzierten Sammlungstypen mithilfe des /reference-Schalters (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 Auflistungstypen angeben, auf die verwiesen wird, indem Sie den Schalter "/collectionType " mehrmals verwenden.

Um beispielsweise zu bewirken, dass alle Listen als generisch 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 Auflistung wird diese Liste mit referenzierten Auflistungstypen überprüft, und die am besten übereinstimmende Auflistung wird verwendet, wenn eine gefunden wird, entweder als Datentyp (für nicht angepasste Auflistungen) oder als Basistyp, von dem abgeleitet werden soll (für benutzerdefinierte Auflistungen). Wörterbücher werden nur mit Wörterbüchern abgeglichen, während Listen mit Listen abgeglichen werden.

Wenn Sie z. B. "Generic" BindingList<T> und Hashtable der Liste der referenzierten Typen hinzufügen, ähnelt der generierte Clientcode für das vorangehende Beispiel folgendem.

[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 Der referenzierten Sammlungstypen angeben, aber Sie können keine ungültigen Sammlungstypen angeben (z. B. typen ohne Add Methode oder öffentlicher Konstruktor).

Ein geschlossener generischer Typ wird als beste Übereinstimmung betrachtet. (Nicht generische Typen gelten als gleichwertig mit geschlossenen Generika von Object). Wenn z. B. die Typen "Generic" List<T>, "Generic DateTimeBindingList<T> " (open generic) 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

Bei Listensammlungen werden nur die Fälle in der folgenden Tabelle unterstützt.

Referenzierter Typ Vom referenzierten Typ implementierte Schnittstelle Beispiel Typ behandelt als:
Nicht generische oder geschlossene generische (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 (eine beliebige Anzahl von Parametern, die nicht unbedingt mit dem Sammlungstyp übereinstimmen) Geschlossenes Generikum MyType : IList<string>

oder

MyType<T> : IList<string> wobei T=int
Geschlossen generisch (z. B. IList<string>)
Geschlossenes generisches Modell mit beliebiger 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 geöffnet Offene generische (z. B. IList<T>)

Wenn ein Typ mehrere Listensammlungsschnittstellen implementiert, gelten die folgenden Einschränkungen:

  • Wenn der Typ Generic-IEnumerable<T> (oder dessen abgeleitete Schnittstellen) mehrmals für unterschiedliche Typen implementiert, wird der Typ nicht als gültiger referenzierter Collection-Typ betrachtet und ignoriert. Dies gilt auch dann, wenn einige Implementierungen ungültig sind oder offene Generika verwenden. Beispielsweise würde ein Typ, der "Generic IEnumerable<T> von int" und "Generic IEnumerable<T> von T" implementiert, niemals als referenzierte Sammlung von int oder einem anderen Typ verwendet werden, unabhängig davon, ob der Typ eine Add-Methode hat, die int akzeptiert, oder eine Add-Methode, die einen Parameter vom Typ T akzeptiert, oder beides.

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

Bei Wörterbuchsammlungen werden nur die Fälle in der folgenden Tabelle unterstützt.

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

oder

MyType<T> : IDictionary wobei T=int
Geschlossenes generisches IDictionary<object,object>
Geschlossen generisch (beliebige Anzahl von Parametern) IDictionary<TKey,TValue>, geschlossen MyType<T> : IDictionary<string, bool> wobei T=int Geschlossen generisch (z. B. IDictionary<string,bool>)
Geschlossen generisch (beliebige Anzahl von 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 (beliebige Anzahl von Parametern) Generische IDictionary<TKey,TValue>, sowohl Schlüssel als auch Wert sind offen und verwenden jeweils einen der 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 allgemein (zwei Parameter) Generic IDictionary<TKey,TValue>, Open, verwendet beide generischen Parameter des Typs in der Reihenfolge, in der sie erscheinen. MyType<K,V> : IDictionary<K,V>, K und V sind beide geöffnet Offene generische (z. B. IDictionary<K,V>)

Wenn der Typ sowohl IDictionary als auch Generic IDictionary<TKey,TValue> implementiert, wird nur Generic IDictionary<TKey,TValue> berücksichtigt.

Das Verweisen auf partielle 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 IntegerReferencedCollectionTypeshinzufü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 im Schema einen Typ gibt, der das Problem mit Duplikaten offenlegt. Wenn das zu importierende Schema keine Listen mit ganzen Zahlen enthält, ist es zulässig, sowohl die generische List<T> von Integer als auch die generische Sammlung von Integer im ReferencedCollectionTypes zu haben, aber keine von ihnen hat eine Auswirkung.

Erweiterte Sammlungsregeln

Serialisieren von Sammlungen

Es folgt eine Liste der Sammlungsregeln für die Serialisierung:

  • Das Kombinieren von Sammlungstypen (Sammlungen von Sammlungen) ist zulässig. Verzweigte Arrays werden als Sammlungen von Sammlungen behandelt. Mehrdimensionale Arrays werden nicht unterstützt.

  • Byte-Arrays und Arrays von XmlNode sind spezielle Arraytypen, die als primitive Datentypen behandelt werden, nicht als Auflistungen. Das Serialisieren eines Bytearrays führt zu einem einzelnen XML-Element, das einen Block von Base64-codierten Daten enthält, anstelle eines separaten Elements für jedes Byte. Weitere Informationen dazu, wie ein Array von XmlNode behandelt wird, 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 Datentyp behandelt, nicht als Sammlung.

  • Wenn ein Sammlungstyp die IXmlSerializable Schnittstelle implementiert, gelten die folgenden Regeln, je nach Typ myType:IList<string>, IXmlSerializable:

    • Wenn der deklarierte Typ lautet IList<string>, wird der Typ als Liste serialisiert.

    • Wenn der deklarierte Typ lautet myType, wird er als IXmlSerializableserialisiert.

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

  • Auflistungen werden mithilfe der in der folgenden Tabelle gezeigten Methoden serialisiert und deserialisiert.

Sammlungstyp implementiert Methoden, die bei der Serialisierung aufgerufen werden 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> Aufzähler Generisches Hinzufügen
IList IList Indexerstellung Add
Generisches IEnumerable<T> GetEnumerator Eine nicht statische Methode namens Add, die einen Parameter des entsprechenden Typs nimmt (der Typ des generischen Parameters oder eines seiner Basistypen). Eine solche Methode muss für den Serialisierer vorhanden sein, um einen Sammlungstyp sowohl während der Serialisierung als auch der Deserialisierung als Sammlung zu behandeln.
IEnumerable (und daher ICollection, die sich daraus ableitet) GetEnumerator Eine nicht-statische Methode namens Add, die einen Parameter vom Typ Object akzeptiert. Eine solche Methode muss für den Serialisierer vorhanden sein, um einen Sammlungstyp sowohl während der Serialisierung als auch der Deserialisierung als Sammlung zu behandeln.

In der obigen Tabelle sind Auflistungsschnittstellen in absteigender Reihenfolge der Rangfolge aufgeführt. Dies bedeutet beispielsweise, dass, wenn ein Typ sowohl IList als auch Generic IEnumerable<T> implementiert, die Sammlung gemäß den IList-Regeln serialisiert und deserialisiert wird.

  • Bei der Deserialisierung werden alle Auflistungen deserialisiert, indem zuerst eine Instanz des Typs erstellt wird, indem der parameterlose Konstruktor aufgerufen wird, der für den Serialisierer vorhanden sein muss, um einen Sammlungstyp sowohl während der Serialisierung als auch während der Deserialisierung als Sammlung zu behandeln.

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

  • Sammlungstypen können das SerializableAttribute Attribut auf sie angewendet haben und die ISerializable Schnittstelle implementieren. Beide werden ignoriert. Wenn der Typ jedoch nicht vollständig den Anforderungen des Sammlungstyps entspricht (z. B. fehlt die Methode), wird der Add Typ nicht als Sammlungstyp betrachtet, und somit werden das SerializableAttribute Attribut und die ISerializable Schnittstelle verwendet, um zu bestimmen, ob der Typ serialisiert werden kann.

  • Durch Anwenden des CollectionDataContractAttribute -Attributs auf eine Sammlung, um diese anzupassen, wird der oben genannte SerializableAttribute -Fallback-Mechanismus entfernt. Wenn eine benutzerdefinierte Auflistung die Anforderungen des Sammlungstyps nicht erfüllt, wird stattdessen eine InvalidDataContractException Ausnahme ausgelöst. Die Ausnahmezeichenfolge enthält häufig Informationen, die erläutern, warum ein bestimmter Typ nicht als gültige Auflistung betrachtet wird (keine Add Methode, kein parameterloser Konstruktor usw.), daher ist es häufig nützlich, das CollectionDataContractAttribute Attribut für Debuggingzwecke anzuwenden.

Sammlungsbenennung

Hier ist eine Liste von Benennungsregeln für Sammlungen:

  • 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 mit „Namespace“ überschrieben. Typen, die integrierten XSD-Typen zugeordnet sind, sowie die Typen char, Timespan und Guid werden zu diesem Zweck als Grundtypen betrachtet.

  • Der Standardnamespace für Sammlungstypen, die nicht primitive Typen enthalten, sofern er nicht mithilfe von Namespace außer Kraft gesetzt wird, entspricht dem Datenvertragsnamespace des Typs, der in der Auflistung enthalten ist.

  • 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. Beispielsweise lautet der Datenvertragsname für eine generische Liste ganzzahliger Zahlen "ArrayOfint". Denken Sie daran, dass der Name des Datenvertrags Object "anyType" lautet, sodass der Datenvertragsname nicht generischer 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 der Schlüssel oder die Werttypen nicht grundtypisch sind, wird außerdem ein Namespacehash der Datenvertragsnamespaces der Schlüssel- und Werttypen an den Namen angefügt. Weitere Informationen zu Namespacehashes finden Sie unter "Datenvertragsnamen".

Jeder Vertrag für Wörterbuchsammlungsdaten verfügt über einen Vertrag für Begleitdaten, der einen Eintrag im Wörterbuch darstellt. Der Name ist identisch mit dem Wörterbuchdatenvertrag, mit Ausnahme des Präfixes "ArrayOf", und der Namespace ist identisch mit dem Vertrag für Wörterbuchdaten. Beispielsweise kann für den "ArrayOfKeyValueOfstringint"-Wörterbuchdatenvertrag der "KeyValueofstringint"-Datenvertrag einen Eintrag im Wörterbuch darstellen. Sie können den Namen dieses Datenvertrags mithilfe der ItemName Eigenschaft anpassen, wie im nächsten Abschnitt beschrieben.

Allgemeine Benennungsregeln für Typs, wie unter "Datenvertragsnamen" beschrieben, gelten vollständig für Sammlungstypen; Das heißt, Sie können geschweifte Klammern innerhalb von Name verwenden, um generische Typparameter anzugeben. Zahlen innerhalb der geschweiften Klammern beziehen sich jedoch auf generische Parameter und nicht auf Typen, die in der Auflistung enthalten sind.

Sammlungsanpassung

Die folgenden Verwendungen des CollectionDataContractAttribute Attributs sind verboten und führen zu einer InvalidDataContractException Ausnahme:

Polymorphismusregeln

Wie bereits erwähnt, kann das Anpassen von Sammlungen mithilfe des CollectionDataContractAttribute Attributs die Austauschbarkeit der Sammlung beeinträchtigen. Zwei angepasste Auflistungstypen können nur dann als gleichwertig betrachtet werden, wenn name, Namespace, Elementname sowie Schlüssel- und Wertnamen übereinstimmen (wenn dies Wörterbuchsammlungen sind).

Aufgrund von Anpassungen kann es passieren, dass versehentlich ein Datenvertrag für Sammlungen verwendet wird, wenn ein anderer erwartet wird. Dies sollte vermieden werden. Sehen Sie sich die folgenden Typen an.

[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 Marks1testMarkszugewiesen werden. Marks2 sollte jedoch nicht verwendet werden, da sein Datenvertrag nicht als gleichwertig mit dem IList<int> Datenvertrag angesehen wird. Der Datenvertragsname ist "Marks2" und nicht "ArrayOfint", und der Name des wiederholten Elements ist "<mark>" und nicht "<int>".

Die Regeln in der folgenden Tabelle gelten für die polymorphe Zuordnung von Auflistungen.

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

Anpassungen werden vorgenommen.
Schnittstelle für Sammlungen Vertragsname wird nicht serialisiert. Vertragsname wird nicht serialisiert.

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

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

Anpassung des zugewiesenen Typs wird verwendet. **

In diesem Fall erfolgt die Anpassung über die NetDataContractSerializer Klasse. 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 den Fällen, in denen der Vertragsname serialisiert wird, sollte der zugewiesene Sammlungstyp in der Liste der bekannten Typen enthalten sein. Das Gegenteil ist auch wahr: In den Fällen, in denen der Name nicht serialisiert wird, ist das Hinzufügen des Typs zur Liste bekannter Typen nicht erforderlich.

Ein Array eines abgeleiteten Typs kann einem Array eines Basistyps zugewiesen werden. In diesem Fall wird der Vertragsname für den abgeleiteten Typ für jedes wiederholte Element serialisiert. Wenn z. B. ein Typ Book vom Typ LibraryItemabgeleitet ist, können Sie einem Array von Book einem Array von LibraryItemzuweisen. Dies gilt nicht für andere Sammlungstypen. Sie können z. B. eine Generic List of Book nicht einem Generic List of LibraryItemzuweisen. Sie können jedoch ein Generic List of LibraryItem zuweisen, das Book Instanzen enthält. Sowohl im Array-Fall als auch im Nicht-Array-Fall sollte Book in der Liste der bekannten Typen enthalten sein.

Erhaltung von Sammlungen und Objektreferenzen

Wenn ein Serialisierer in einem Modus funktioniert, in dem Objektverweise beibehalten werden, gilt die Objektreferenzerhaltung auch für Auflistungen. Insbesondere wird die Objektidentität sowohl für ganze Sammlungen als auch für einzelne Elemente, die in Sammlungen enthalten sind, beibehalten. Bei Wörterbüchern wird die Objektidentität sowohl für die Schlüssel-Wert-Paarobjekte als auch für die einzelnen Schlüssel- und Wertobjekte beibehalten.

Siehe auch