集合是特定類型的項目清單。 在 .NET Framework 中,這類清單可以使用陣列或各種其他類型來表示(泛型清單、泛型 BindingList<T>、StringCollection或 ArrayList)。 例如,集合可能會保存指定客戶的位址清單。 這些集合稱為 清單集合,不論其實際類型為何。
有一種特殊形式的集合,代表一個項目(“鍵”)與另一個項目之間的關聯(“值”)。 在 .NET Framework 中,這些是以 和 Hashtable 泛型字典等類型表示。 例如,關聯集合可能會將城市 (“key”) 對應至其人口 (“value” )。 這些集合稱為 字典集合,不論其實際類型為何。
集合會在數據合約模型中接受特殊處理。
實作 IEnumerable 介面的類型,包括數位和泛型集合,都會辨識為集合。 其中,實作 IDictionary 或泛型 IDictionary<TKey,TValue> 介面的類型是字典集合;所有其他類型都是清單集合。
下列各節會詳細討論集合類型的其他需求,例如呼叫 Add
方法和無參數建構函式。 這可確保集合類型可以同時串行化和還原串行化。 這表示某些集合不受直接支援,例如泛型 ReadOnlyCollection<T> (因為它沒有無參數建構函式)。 不過,如需規避這些限制的資訊,請參閱本主題稍後的<使用集合介面類型和 Read-Only 集合>一節。
集合中包含的型別必須是資料契約型別,或其他可序列化形式。 如需詳細資訊,請參閱 數據合約串行化程式支援的類型。
如需什麼是和未被視為有效集合的詳細資訊,以及集合的串行化方式,請參閱本主題一節中有關串行化集合的資訊。
可交換的集合
相同型別的所有清單集合都會被視為具有相同的數據合約(除非使用 屬性進行自定義 CollectionDataContractAttribute ,如本主題稍後所述)。 因此,例如,下列數據合約相等。
[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
這兩個數據合約都會產生類似下列程式代碼的 XML。
<PurchaseOrder>
<customerName>...</customerName>
<items>
<Item>...</Item>
<Item>...</Item>
<Item>...</Item>
...
</items>
<comments>
<string>...</string>
<string>...</string>
<string>...</string>
...
</comments>
</PurchaseOrder>
集合可互換性可讓您使用針對伺服器效能優化的集合類型,以及設計成系結至用戶端上使用者介面元件的集合類型。
與清單集合類似,具有相同索引鍵和實值型別的所有字典集合都會被視為具有相同的數據合約(除非由 屬性自定義 CollectionDataContractAttribute )。
就集合等價而言,只有數據合約類型才重要,而不是 .NET 類型。 也就是說,如果 Type1 和 Type2 有對等的數據合約,Type1 的集合就會被視為相當於 Type2 的集合。
非泛型集合會被視為具有與 型 Object
別泛型集合相同的數據合約。 例如,ArrayList的數據合約和List<T>的泛型Object
數據合約是相同的。
使用集合介面類型和 Read-Only 型集合
集合介面類型(IEnumerable、、 IDictionary泛型 IDictionary<TKey,TValue>或衍生自這些介面的介面)也被視為具有集合數據合約,相當於收集實際收集型別的數據合約。 因此,可以將被序列化的類型宣告為集合介面類型,結果與實際使用集合類型時相同。 例如,下列數據合約相等。
[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
在串行化期間,當宣告的類型是介面時,使用的實際實例類型可以是實作該介面的任何類型。 先前討論的限制(具有無參數建構函式和 Add
方法)不適用。 例如,您可以將 Customer2 中的位址設定為一般地址的 ReadOnlyCollection<T> 實例,即使您無法直接宣告泛型 ReadOnlyCollection<T>類型的數據成員也一樣。
進行反序列化期間,當所宣告的類型是介面時,序列化引擎會選擇一個實作該介面的類型,並實例化該類型。 已知型別機制(如 數據合約已知類型中所述)在這裡沒有作用:類型的選擇是內建在 WCF 中。
自訂集合類型
您可以使用 CollectionDataContractAttribute 屬性來自定義集合類型,此屬性有多種用途。
請注意,自定義集合類型會危害集合可互換性,因此通常建議盡可能避免套用此屬性。 如需此問題的詳細資訊,請參閱本主題後面的「進階收集規則」一節。
集合數據合約命名
命名集合類型的規則類似於為一般數據合約類型命名的規則,如 數據合約名稱中所述,雖然存在一些重要的差異:
屬性 CollectionDataContractAttribute 是用來自定義名稱,而不是 DataContractAttribute 屬性。 屬性 CollectionDataContractAttribute 也有
Name
和Namespace
屬性。CollectionDataContractAttribute未套用屬性時,集合類型的預設名稱和命名空間取決於集合中包含的型別名稱和命名空間。 它們不會受到集合類型本身的名稱和命名空間影響。 如需範例,請參閱下列類型。
public CustomerList1 : Collection<string> {} public StringList1 : Collection<string> {}
這兩種類型的數據合約名稱都是 「ArrayOfstring」,而不是 「CustomerList1」 或 “StringList1”。 這表示在根層級上串行化這些類型中的任何一種會產生類似於下列程式代碼的 XML。
<ArrayOfstring>
<string>...</string>
<string>...</string>
<string>...</string>
...
</ArrayOfstring>
已選擇此命名規則,以確保代表字串清單的任何非自定義類型都有相同的數據合約和 XML 表示法。 這可讓集合互換性成為可能。 在此範例中,CustomerList1 和 StringList1 完全可互換。
不過,套用屬性時 CollectionDataContractAttribute ,即使屬性上未設定任何屬性,集合也會變成自定義的集合數據合約。 然後,集合數據合約的名稱和命名空間取決於集合類型本身。 如需範例,請參閱下列類型。
[CollectionDataContract]
public class CustomerList2 : Collection<string> {}
<CollectionDataContract()>
Public Class CustomerList2
Inherits Collection(Of String)
End Class
串行化時,產生的 XML 如下所示。
<CustomerList2>
<string>...</string>
<string>...</string>
<string>...</string>
...
</CustomerList2>
請注意,這不再等於非自定義型別的 XML 表示法。
您可以使用
Name
和Namespace
屬性進一步自定義命名。 請參閱下列類別。[CollectionDataContract(Name="cust_list")] public class CustomerList3 : Collection<string> {}
<CollectionDataContract(Name:="cust_list")> Public Class CustomerList3 Inherits Collection(Of String) End Class
產生的 XML 如下所示。
<cust_list>
<string>...</string>
<string>...</string>
<string>...</string>
...
</cust_list>
如需詳細資訊,請參閱本主題後面名為「進階收集規則」的章節。
自訂清單集合中的重複項目名稱
清單集合包含重複的項目。 一般而言,每個重複項目根據集合中所含類型的數據合約名稱,作為命名的元素來表示。
在範例中 CustomerList
,集合包含字串。 字串基本類型的數據合約名稱是 「string」,因此重複專案是 「<string>」。
不過,使用 ItemName 屬性上的 CollectionDataContractAttribute 屬性,可以自定義這個重複的項目名稱。 如需範例,請參閱下列類型。
[CollectionDataContract(ItemName="customer")]
public class CustomerList4 : Collection<string> {}
<CollectionDataContract(ItemName:="customer")>
Public Class CustomerList4
Inherits Collection(Of String)
End Class
產生的 XML 如下所示。
<CustomerList4>
<customer>...</customer>
<customer>...</customer>
<customer>...</customer>
...
</CustomerList4>
重複元素的命名空間一律與集合資料契約的命名空間相同,可以使用Namespace
屬性來自定義,如前面所述。
自定義詞典集合
字典集合基本上是條目清單, 其中每個條目都有一個索引鍵,後面接著一個值項。 如同一般清單,您可以使用 ItemName 屬性來變更對應至重複元素的項目名稱。
此外,您可以使用 KeyName 和 ValueName 屬性來變更代表索引鍵和值的項目名稱。 這些專案的命名空間與集合數據合約的命名空間相同。
如需範例,請參閱下列類型。
[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
串行化時,產生的 XML 如下所示。
<CountriesOrRegionsWithCapitals>
<entry>
<countryorregion>USA</countryorregion>
<capital>Washington</capital>
</entry>
<entry>
<countryorregion>France</countryorregion>
<capital>Paris</capital>
</entry>
...
</CountriesOrRegionsWithCapitals>
如需有關字典集的詳細資訊,請參閱本主題後面的「進階集合規則」部分。
集合和已知類型
當您以多型方式取代其他集合或集合介面時,不需要將集合類型新增至已知型別。 例如,如果您宣告 類型的 IEnumerable 數據成員,並使用它來傳送 的實例 ArrayList,則不需要新增 ArrayList 至已知型別。
當您以多型方式使用集合來取代非集合類型時,必須將這些集合新增至已知類型。 例如,如果您宣告類型為 Object
的數據成員,並用它來傳送 ArrayList 的實例,請將 ArrayList 加入至已知型別列表中。
這不允許您以多型方式串行化任何對等集合。 例如,當您在上述範例中新增 ArrayList 至已知類型清單時,這不會讓您指派 Array of Object
類別,即使它具有相等的數據合約也一樣。 這與一般已知型別在非集合型別上的串行化行為並無不同,但在集合的情況下尤其重要,因為集合的內容通常是相同的。
在串行化期間,在指定數據合約的任何指定範圍中,只能知道一個類型,而對等集合全都有相同的數據合約。 這表示,在上一個範例中,您無法在相同範圍中同時將 ArrayList 和 Array of Object
新增到已知類型。 同樣地,這相當於非集合型別的已知型別行為,但對集合而言尤其重要。
集合內容可能也需要已知類型。 例如,如果 ArrayList 實際上包含Type1
和Type2
的實例,則應將這些型別加入至已知型別。
下列範例示範使用集合和已知型別正確建構的物件圖形。 此範例有點經過嘗試,因為在實際的應用程式中,您通常不會將下列數據成員 Object
定義為 ,因此沒有任何已知的類型/多型問題。
[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
在還原串行化上,如果宣告的類型是集合類型,則不論實際傳送的類型為何,宣告的類型都會具現化。 如果宣告的類型是集合介面,還原串行化程式會挑選要具現化的型別,而不考慮已知型別。
此外,在反序列化時,如果宣告的類型不是集合類型,但正在傳送集合類型,則會從已知類型清單中選擇一個相符的集合類型。 可以將集合介面類型新增至還原串行化上的已知型別清單。 在此情況下,反序列化引擎會再次選擇要實例化的類型。
Collections 和 NetDataContractSerializer 類別
當類別 NetDataContractSerializer 正在使用中時,非自定義集合類型(不含 CollectionDataContractAttribute 屬性)不會失去其特殊意義。
使用SerializableAttribute屬性標示的未自訂集合類型仍可根據NetDataContractSerializer屬性或SerializableAttribute介面規則由ISerializable類別進行串行化。
即使類別正在使用中 NetDataContractSerializer ,自定義集合類型、集合介面和陣列仍會被視為集合。
集合和架構
所有對等集合在 XML 架構定義語言 (XSD) 架構中都有相同的表示法。 因此,您通常不會在產生的用戶端程式代碼中取得與伺服器上相同的集合類型。 例如,伺服器可能會使用具有 Integer 資料成員泛型 List<T> 的數據合約,但在產生的用戶端程式代碼中,相同的數據成員可能會成為整數數位。
字典集合會透過 WCF 特定的架構標註指出它們是字典;否則,它們與簡單的鍵值對列表無異。 如需如何在數據合約架構中表示集合的確切描述,請參閱 數據合約架構參考。
根據預設,不會針對匯入程式代碼中的非自定義集合產生類型。 列表集合類型的數據成員會匯入為陣列,而字典集合類型的數據成員會匯入為泛型字典。
不過,針對自定義集合,會產生不同的類型,並以 CollectionDataContractAttribute 屬性標示。 (架構中的自定義集合類型是不使用預設命名空間、名稱、重複元素名稱或鍵/值元素名稱的集合類型。這些類型是衍生自清單類型的泛型 List<T> 以及字典類型的泛型字典的空型別。)
例如,您可能在伺服器上有下列類型。
[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
當再次匯出和匯入架構時,產生的用戶端程式代碼會類似下列專案(欄位會顯示,而不是屬性,以方便閱讀)。
[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
您可能想要在產生的程式代碼中使用與預設程式碼不同的類型。 例如,您可能想要針對數據成員使用泛型 BindingList<T> 而非一般數位列,使其更容易系結至使用者介面元件。
若要選擇要產生的集合類型,請在匯入架構時,將您想要使用的集合類型清單傳遞至 ReferencedCollectionTypes 對象的屬性 ImportOptions 。 這些類型稱為 參考的集合類型。
參考泛型型別時,它們必須是完全開啟的泛型或完全封閉的泛型。
備註
使用 Svcutil.exe 工具時,可以使用 /collectionType 命令行參數來完成此參考(簡短形式: /ct)。 請記住,您也必須使用 /reference 切換來指定參考集合類型的程式集(簡短形式:/r)。 如果類型為泛型,則必須後面加上后引號和泛型參數的數目。 後引號 (') 不會與單引號 (') 字元混淆。 您可以使用 /collectionType 參數多次指定多個參考的集合類型。
例如,若要將所有清單匯入為泛型 List<T>。
svcutil.exe MyService.wsdl MyServiceSchema.xsd /r:C:\full_path_to_system_dll\System.dll /ct:System.Collections.Generic.List`1
匯入任何集合時,會掃描此參考集合類型清單,如果找到集合,則會使用最相符的集合,無論是作為數據成員類型(針對非自定義集合),還是作為衍生自的基底類型(針對自定義集合)。 字典只會與字典匹配,而清單則與清單匹配。
例如,如果您將 Generic BindingList<T> 和 Hashtable 新增至參考型別清單,則上述範例產生的用戶端程式代碼類似下列。
[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
您可以將集合介面類型指定為參考集合類型的一部分,但無法指定無效的集合類型(例如沒有 Add
方法或公用建構函式的集合類型)。
封閉式泛型會被視為最佳比對。 (非泛型型別被視為相當於的 Object
封閉式泛型)。 例如,如果類型 GenericList<T>、GenericDateTime(開放泛型),和BindingList<T>是參考的集合類型,則會生成以下內容。
[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
針對清單集合,僅支援下表中的情況。
參考的類型 | 由參考型別實作的介面 | 範例 | 將類型視為: |
---|---|---|---|
非泛型或封閉式泛型 (任意數目的參數) | 非泛型 | MyType : IList 或 MyType<T> : IList 其中 T= int |
封閉的 Object 泛型 (例如, IList<object> ) |
非泛型或封閉式泛型(不一定符合集合類型的任意數目參數) | 封閉式泛型 | MyType : IList<string> 或 MyType<T> : IList<string> 其中 T=int |
封閉式泛型 (例如, IList<string> ) |
具有任意數目參數的封閉泛型 | 使用任一類型的參數開啟泛型 | MyType<T,U,V> : IList<U> where T= int , U=string , V=bool |
封閉式泛型 (例如, IList<string> ) |
使用一個參數開啟泛型 | 使用類型的 參數開啟泛型 |
MyType<T> : IList<T> ,T 是開啟的 |
開啟泛型 (例如, IList<T> ) |
如果類型實作一個以上的清單集合介面,則適用下列限制:
如果類型針對不同類型的實作泛型 IEnumerable<T> (或其衍生介面)多次,則類型不會被視為有效的參考集合類型,而且會被忽略。 即使某些實作無效或使用開放式泛型,也是如此。 例如,實作泛型IEnumerable<T>的
int
型別以及實作泛型IEnumerable<T>的 T 型別,絕不會被用作int
的參考集合或任何其他類型的集合,無論該型別是否有接受Add
方法int
或接受 T 型別之參數的Add
方法,或兩者皆有。如果型別同時實作泛型集合介面和 IList,則除非該泛型集合介面是型別 Object 的閉合泛型,否則該型別絕不會被用作參考的集合型別。
對於詞典集合,僅支援下表中的情形。
參考的類型 | 由參考型別實作的介面 | 範例 | 將類型視為 |
---|---|---|---|
非泛型或封閉式泛型 (任意數目的參數) | IDictionary | MyType : IDictionary 或 MyType<T> : IDictionary 其中 T=int |
封閉式泛型 IDictionary<object,object> |
封閉式泛型 (任意數目的參數) | IDictionary<TKey,TValue>閉合 |
MyType<T> : IDictionary<string, bool> 其中 T=int |
封閉式泛型 (例如, IDictionary<string,bool> ) |
封閉式泛型 (任意數目的參數) | 泛型 IDictionary<TKey,TValue> 中,其中一個鍵或值已確定,但另一個仍在使用其中一個類型的參數。 |
MyType<T,U,V> : IDictionary<string,V> 其中 T=int , U=float , V=bool 或 MyType<Z> : IDictionary<Z,bool> 其中 Z=string |
封閉式泛型 (例如, IDictionary<string,bool> ) |
封閉式泛型 (任意數目的參數) | 泛型 IDictionary<TKey,TValue>,索引鍵和值都是不固定的,每個都使用型別參數中的一個 |
MyType<T,U,V> : IDictionary<V,U> where T=int , U=bool , V=string |
封閉式泛型 (例如, IDictionary<string,bool> ) |
開啟泛型 (兩個參數) | 泛型IDictionary<TKey,TValue>,打開,按其出現的順序使用該型別的兩個泛型參數。 |
MyType<K,V> : IDictionary<K,V> 、K 和 V 皆已開啟 |
開啟泛型 (例如, IDictionary<K,V> ) |
如果型別同時實作 IDictionary 和 泛型 IDictionary<TKey,TValue>,則只會考慮泛型 IDictionary<TKey,TValue> 。
不支持參考部分泛型型別。
禁止重複項目,例如,您不能同時將 GenericList<T> 的 Integer
和 Generic 集合的 Integer
新增至ReferencedCollectionTypes,因為這樣在架構中找到整數清單時,將無法判斷應使用哪一個。 只有當架構中存在公開重複項目的型別時,才會偵測重複項目。 例如,如果匯入的架構不包含整數清單,則允許在List<T>中同時擁有 Generic Integer
和 Generic Collection Integer
,但兩者都沒有作用。
進階集合規則
序列化集合
以下是序列化集合規則的清單:
允許結合集合類型(擁有多層集合的集合)。 鋸齒狀陣列會被視為多個集合組成的集合。 不支援多維度陣列。
位元組陣列和XmlNode陣列是特殊陣列類型,會被視為基本類型,而不是集合。 串行化位元組陣列會產生單一的 XML 元素,其中包含以 Base64 編碼的數據區塊,而不是每個位元組的個別元素。 如需有關如何處理陣列 XmlNode 的詳細資訊,請參閱 資料合約中的 XML 和 ADO.NET 類型。 當然,這些特殊類型本身可以參與集合:位元組陣列會產生多個 XML 元素,每個元素都包含Base64編碼數據的區塊。
如果屬性 DataContractAttribute 套用至集合類型,則類型會被視為一般數據合約類型,而不是做為集合。
如果集合類型實作 IXmlSerializable 介面,則會套用下列規則,並指定類型
myType:IList<string>, IXmlSerializable
:如果宣告的型別為
IList<string>
,則類型會串行化為清單。如果宣告的類型為
myType
,則會串行化為IXmlSerializable
。如果宣告的型別為
IXmlSerializable
,則會串行化為IXmlSerializable
,但只有在您新增myType
至已知型別清單時。
集合會使用下表所示的方法進行串行化和還原串行化。
集合類型實作 | 序列化時呼叫的方法 | 在反序列化時呼叫的方法 |
---|---|---|
通用 IDictionary<TKey,TValue> |
get_Keys 、get_Values |
一般新增 |
IDictionary |
get_Keys 、get_Values |
Add |
通用 IList<T> | 泛型 IList<T> 索引器 | 一般新增 |
通用 ICollection<T> | 列舉器 | 一般新增 |
IList | IList 索引器 | Add |
通用 IEnumerable<T> | GetEnumerator |
稱為的非靜態方法 Add ,採用適當類型的一個參數(泛型參數的類型或其基底型別之一)。 序列化器在序列化和反序列化過程中,必須有方法能將集合型別視為集合。 |
IEnumerable (因此 ICollection,衍生自它) | GetEnumerator |
稱為的非靜態方法 Add ,採用類型 Object 為的一個參數。 序列化器在序列化和反序列化過程中,必須有方法能將集合型別視為集合。 |
上表會依優先順序遞減順序列出集合介面。 例如,如果型別同時實作 IList 和 Generic IEnumerable<T>,則集合會根據 IList 規則串行化和還原串行化:
在反序列化過程中,首先會透過呼叫無參數建構子建立類型實例以反序列化所有集合。這是必要的,因為序列化器需要在序列化和反序列化期間將集合類型正確視作集合。
如果同一個泛型集合介面被多次實作(例如,如果類型同時實作 Generic ICollection<T> of
Integer
和 Generic ICollection<T> of String),而且找不到具有較高優先順序的介面,則該集合不會被視為有效的集合。集合類型可以套 SerializableAttribute 用 屬性,而且可以實作 ISerializable 介面。 這兩者都會被忽略。 不過,如果型別未完全符合集合類型需求(例如
Add
,遺漏 方法),則類型不會被視為集合類型,因此 SerializableAttribute 會使用 屬性和 ISerializable 介面來判斷型別是否可以串行化。將 CollectionDataContractAttribute 屬性套用至集合,以自定義它會移除先前的 SerializableAttribute 後援機制。 相反地,如果自定義集合不符合集合類型需求, InvalidDataContractException 則會擲回例外狀況。 例外狀況字串通常包含一些資訊,說明為何指定類型未被視為有效的集合(沒有方法、無
Add
參數建構函式等等),因此通常適用於偵錯目的套用 CollectionDataContractAttribute 屬性。
集合命名
以下是集合命名規則的清單:
所有字典集合的資料合約,以及包含基本類型的清單集合資料合約的預設命名空間,除非使用 Namespace 覆寫,都是
http://schemas.microsoft.com/2003/10/Serialization/Arrays
。 對應至內建 XSD 型別的類型,以及char
、Timespan
和Guid
類型,會被視為此目的的基本類型。包含非基本型別之集合型別的預設命名空間,除非使用 Namespace 覆寫,否則與集合中包含的型別數據合約命名空間相同。
除非使用 Name 覆寫,否則清單集合資料合約的預設名稱是字串 「ArrayOf」 與集合中包含的類型資料合約名稱結合。 例如,整數泛型清單的數據合約名稱是 “ArrayOfint”。 請記住,數據合約
Object
的名稱為「anyType」,因此像ArrayList這樣的非泛型清單的數據合約名稱為「ArrayOfanyType」。
除非使用 Name
覆寫,字典集合資料契約的預設名稱是字串 "ArrayOfKeyValueOf",接著是索引鍵類型的資料契約名稱,然後是值類型的資料契約名稱。 例如,String 泛型字典和 Integer 的數據合約名稱是 “ArrayOfKeyValueOfstringint”。 此外,如果索引鍵或實值型別不是基本型別,索引鍵的數據合約命名空間和實值型別的命名空間哈希會附加至名稱。 如需命名空間哈希的詳細資訊,請參閱 數據合約名稱。
每個字典集合數據合約都有一個對應的數據合約,代表字典中的一個條目。 其名稱與字典數據合約相同,但 「ArrayOf」 前置詞除外,其命名空間與字典數據合約的名稱相同。 例如,對於 “ArrayOfKeyValueOfstringint” 字典數據合約,“KeyValueofstringint” 數據合約表示字典中的一個項目。 如下一節所述,您可以使用ItemName
屬性來自訂此資料合約的名稱。
一般類型命名規則,如 數據合約名稱中所述,完全適用於集合類型;也就是說,您可以使用 Name 內的大括弧來表示泛型類型參數。 不過,大括弧內的數位是指泛型參數,而不是集合中包含的類型。
集合自定義
下列 CollectionDataContractAttribute 屬性的使用是禁止的,並會導致 InvalidDataContractException 例外:
將 DataContractAttribute 屬性套用至 CollectionDataContractAttribute 已套用屬性的類型,或套用至其中一個衍生型別。
將 CollectionDataContractAttribute 屬性套用至實作此介面的類型 IXmlSerializable。
將 CollectionDataContractAttribute 屬性套用至非集合類型。
試著在套用至非字典類型的屬性上KeyName設定 ValueName 或 CollectionDataContractAttribute 。
多型規則
如先前所述,使用 CollectionDataContractAttribute 屬性自定義集合可能會干擾集合的可互換性。 只有當兩個自定義集合類型的名稱、命名空間、項目名稱,以及索引鍵和值名稱(如果這些是字典集合)相匹配時,它們才能被視為相等。
由於自訂設定,可能會不小心將某個集合數據合約用於預期應使用另一個合約的情況。 這應該避免。 請參閱下列類型。
[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
在這種情況下,可以將Marks1
的實例指派給testMarks
。 不過, Marks2
不應該使用,因為其數據合約不被視為相當於 IList<int>
數據合約。 數據合約名稱為 「Marks2」 而非 「ArrayOfint」 ,而重複的專案名稱為 「<mark>」 而非 「<int>」。
下表中的規則適用於集合的多型指派。
宣告型別 | 指派非自定義集合 | 指派自定義集合 |
---|---|---|
物體 | 該合約名稱已序列化。 | 該合約名稱已序列化。 使用自定義。 |
集合介面 | 合約名稱未串行化。 | 合約名稱未串行化。 未使用自定義。* |
非自訂集合 | 合約名稱未串行化。 | 該合約名稱已序列化。 使用自定義。** |
自訂收藏 | 該合約名稱已序列化。 未使用自定義。** | 該合約名稱已序列化。 會使用指派類型的自定義。** |
*使用 類別 NetDataContractSerializer 時,會在此案例中使用自定義。 此 NetDataContractSerializer 類別也會在此案例中串行化實際類型名稱,因此還原串行化會如預期般運作。
**這些案例會導致架構無效的實例,因此應避免。
在合約名稱串行化的情況下,指派的集合類型應該位於已知型別清單中。 相反的情況也是如此:在名稱未串行化的情況下,不需要將類型新增至已知類型清單。
衍生類型的陣列可以指派給基底類型的陣列。 在此情況下,衍生型別的合約名稱會針對每個重複元素進行串行化。 例如,如果型別 Book
是從型別 LibraryItem
衍生而來,您可以將型別為 Book
的陣列指派給型別為 LibraryItem
的陣列。 這不適用於其他集合類型。 例如,您無法將Generic List of Book
指派給Generic List of LibraryItem
。 不過,您可以指定包含 Generic List of LibraryItem
實例的 Book
。 在陣列和非陣列案例中, Book
應該位於已知類型清單中。
集合和對象參考保留
當串行化程式在保留物件參考的模式中運作時,對象參考保留也會套用至集合。 具體而言,物件識別對於整個集合及集合中包含的個別項目均予以保留。 針對字典,對象識別會同時保留索引鍵/值組物件和個別索引鍵和值物件。