在Net中將實現了IEnumerable接口的所有類型(包括數組和泛型)都稱之為集合類型把其中實現了IDictionary接口或泛型IDictionary接口的集合類型稱為字典集合剩下的其他集合類型為列表集合
集合的數據契約(協定)缺省名稱
缺省情況下WCF框架對集合類型是內建支持的也就說你不需要應用任何屬性就可以將集合應用在數據契約(協定)中但前提是集合中的元素必須是應用了DataContractAttribute屬性或者是可序列化的類型這時數據契約(協定)名稱和命名空間就依賴集合中包含的元素的類型的名稱和命名空間了它們不受集合類型本身的名稱和命名空間的影響
缺省集合類型數據契約(協定)的格式是(不包括+)
列表集合
名稱ArrayOf+集合中包含的元素類型
循環元素名稱集合中包含的元素類型
字典集合
名稱ArrayOfKeyValueOf+集合中Key的類型+集合中包含的對象類型
循環元素名稱KeyValueOf+集合中Key的類型+集合中包含的對象類型
例如
MyCollection : IList{…}的數據契約名稱就是ArrayOfint
MyCollection : ICollection{…}的數據契約名稱就是ArrayOfint
MyDictionary : Dictionary{…}的數據契約名稱就是ArrayOfKeyValueOfintint
MyCollection : ArrayList{…}的數據契約名稱就是ArrayOfanyType
MyDictionary : Dictionary{…}的數據契約名稱就是ArrayOfKeyValueOfintanyType
注意如果是object的話使用的是anyType因為在Schema中所有類型的基類是anyType
如果集合是應用於某個數據契約類型中時那麼它的名稱將是字段名稱如下面Customer的定義以及序列化後的表示
[DataContract]
public class Customer
{
[DataMember]
public List<string> addresses = new List<string> {BeijingShangHai };
[DataMember]
public Dictionary<int object> telephones = new Dictionary<int object> {
{ }
{ } };
}
<Customer xmlns:i=instance
xmlns=>
<addresses xmlns:dp=>
<dp:string>Beijing</dp:string>
<dp:string>ShangHai</dp:string>
</addresses>
<telephones
xmlns:dp=>
<dp:KeyValueOfintanyType>
<dp:Key></dp:Key>
<dp:Value xmlns:dp=
i:type=dp:string></dp:Value>
</dp:KeyValueOfintanyType>
<dp:KeyValueOfintanyType>
<dp:Key></dp:Key>
<dp:Value xmlns:dp=
i:type=dp:string></dp:Value>
</dp:KeyValueOfintanyType>
</telephones>
</Customer>
集合的契約等價
在WCF中應用了CollectionDataContractAttribute屬性的集合稱之為定制數據契約集合否則為非定制數據契約集合不管是非定制數據契約集合還是定制數據契約集合只要它們的數據契約名稱和循環元素的名稱都相同(如果是字典集合其Key和Value也要是相同的)我們就說它們是等價的由於非定制數據契約集合的數據契約以及循環元素的名稱由集合中的類型決定所以非定制數據契約集合的數據契約等價遵守以下幾個規則(關於數據契約等價的其他詳細信息請參見我以前的文章WCF Data Contract之契約等價)
相同類型的列表集合被認為具有相同的數據契約(協定)例如List和int[]是等價的
具有相同相同鍵和值類型的所有字典集合也被視為具有相同的數據契約(協定)
接口和實現該接口的具體集合類的數據契約是等價的例如IList和List等價
非泛型集合與Object類型的泛型集合的數據契約等價例如List<Object>與ArrayList等價ArrayList與Object[]也是等價的
這就是為什麼上面的示例中實現IList和ICollection的集合類型的數據契約名稱都是ArrayOfint的原因例如下面兩個數據契約是等價的
[DataContract(Name=Customer)] public class Customer
{
[DataMember] public string customerName;
[DataMember] public Collection<string> addresses;
[DataMember] public string[] telephones;
}[DataContract(Name=Customer)] public class Customer
{
[DataMember] public string customerName;
[DataMember] public ICollection<string> addresses;
[DataMember] public List<string> telephones;
}
注意Collection和ICollection是等價的同時string[]和List也是等價的
所以針對Customer的定義我們可以將實現ICollection接口的任何集合類的實例賦給addresses同樣我們也可以利用在數據契約中定義集合接口的機制來將WCF不支持的集合類型(如ReadOnlyCollection具體見本文中WCF中對集合類型的要求限制一節)來應用到我們的WCF應用中也就是說我們可以將ReadOnlyCollection的實例賦給Customer的addresses
集合與KnowType
集合類型和非集合類型在多態增加KnowType類型方面是不同的關於非集合方面增加KnowType的詳細情況請我以前的文章WCF Data Contract之KnowType集合類型在增加KnowType類型遵守以下規則
集合類型是以多態方式來代替其他集合或集合接口的您不需要將這樣的集合類型添加到KnowType類型中例如上例中Customer類中的addresses你可以將List的實例賦值給它而不需要將List增加到KnowType類型列表中
當您以多態方式使用集合來代替非集合類型時則需要將它們添加到已知類型 例如如果您聲明一個 Object 類型的數據成員並將其用於發送 ArrayList 的一個實例則需要將 ArrayList 添加到已知類型中
等價的集合只能應用KnowTypeAttribute屬性來將其增加到KnowType列表中一次例如不能將ArrayList和Object[]都添加到相同類的KnowType列表中
定制集合的數據契約(協定)
我們可以使用CollectionDataContractAttribute的下列屬性來指定集合的數據契約的相關名稱及命名空間
Name屬性來指定集合數據契約的名稱(如果沒有使用此屬性將使用集合類型的名稱)
Namespace屬性來指定其命名空間
ItemName 屬性來指定循環元素的名稱
針對字典集合還可以用KeyName和ValueName來指定鍵和值的名稱
例如我們將第一節的例子更改成如下所示
[CollectionDataContract(Name = telephones ItemName = telephone
KeyName = Index ValueName = Number)]
public class MyDictionary : Dictionary
{
public new DictionaryEnumerator GetEnumerator()
{
Dictionary innerObject = new Dictionary {
{ }
{ } };
return innerObjectGetEnumerator();
}
}
此類將被序列化成
>
對於定制數據契約的集合類型來說前面所述的非定制數據契約的集合等價規則將失效所以要盡量避免使用CollectionDataContractAttribute
集合的反序列化
缺省情況下使用Svcutilexe生成客戶端代理時列表集合將反序列化成數組字典集合將反序列化成Dictionary泛型我們也可以通過/collectionType 命令行開關(簡寫形式是 /ct)來指定我們希望反序列化的集合類型(請記住您還必須使用 /reference 開關(簡寫形式是 /r)指定引用的集合類型的程序集)如果該類型是泛型則必須在類型後面跟有反引號和泛型參數的數目例如前面的例子中的Customer類可以通過下面的命令在客戶端使用List泛型//localhost:/
/r:C:\WINDOWS\MicrosoftNET\Framework\v\Systemdll
/ct:SystemCollectionsGenericList`
DataContractAttribute和CollectionContractAttribute
對於集合而言WCF框架將隱含地自動的為集合類型應用CollectionDataContractAttribute屬性的這就是為什麼你不需要為集合應用任何屬性就可以在數據契約中使用的原因但要注意
()如果我們新建的集合類型是繼承已有的集合類型如List那麼我們就不能對新建的集合類型應用DataContractAttribute否則運行時會拋出InvalidDataContractException但你可以應用CollectionDataContractAttribute來定制集合類型的數據契約例如[DataContract]public class MyList:List{…}的集合定義將拋出異常
()如果我們新建的集合類型是實現了集合接口例如IListIDictionary的話我們可以對此類型應用DataContractAttribute屬性這樣的話此類型將作為普通的數據契約類型而不是將其作為集合類型來處理也就是WCF框架將只序列化其中應用了DataMemberAttribute屬性的成員當然你也可以不應用任何屬性來讓系統缺省作為集合類型來處理(你也可以使用CollectionDataContractAttribute來定制數據契約)
()針對應用CollectionDataContractAttribute屬性或者缺省不應用任何屬性的集合類型如果其內部有應用了DataMemberAttribute的屬性或字段在序列化時系統將忽略
WCF中對集合類型的要求限制
不是所有的集合類型都可以在WCF中使用只有滿足以下要求才可以使用
該集合類型有一個缺省的構造函數
該集合類型有一個名為Add的方法
這是因為在反序列化集合類型時WCF框架首先調用該集合類型的無參數的構造函數然後通過非靜態的Add方法來將循環元素增加到集合中所以以上限制主要是針對反序列化而設定的
集合中的一些高級規則
WCF框架在序列化時支持集合的集合也支持數組的數組(交錯數組)但不支持多唯數組
字節數組和 XmlNode 數組是特殊的數組類型將被視為基元而不是集合 序列化字節數組會產生單個包含一個 Base 編碼數據塊的 XML 元素而不是為每個字節都生成一個單獨的元素(筆者認為這是為了性能的考慮才這麼處理的)
如果集合類型實現了IXMLSerializable接口假設類型為MyType:IListIXMLSerializable{…}WCF框架將根據在數據契約中聲明的類型來進行序列化如果聲明的是集(接口)如IList那麼該類型將被認為是列表集合來序列化如果聲明的是IXMLSerializable那麼將按照IXMLSerializable來進行序列化當然需要將該類型加到KnowType類型列表中如果聲明的是該類型本身(如MyType)那麼將按照IXMLSerializable的規則來進行序列化
在對集合進行序列化時將調用集合類的GetEnumerator 方法來得到集合的內容在反序列化時將首先調用該集合類型的無參數的構造函數然後通過非靜態的Add方法來將循環元素增加到集合中(注雖然這與大家在MSDN的幫助文檔中看到的不同認為字典集合將調用get_Keys和get_Values以及IList將調用索引器但筆者使用VS驗證時沒有得到以上方法被調用的結論所以筆者認為是MSDN文檔滯後或有誤如果各位看官能得到和MSDN吻合的結論麻煩告訴一聲)
如果集合類型同時應用了Serialized屬性或實現了ISerializable接口WCF框架將忽略它們但是如果集合類型不滿足集合類型要求(例如缺少Add)方法那麼將按照Serialized或ISerializable來處理但如果你對該集合同時應用了CollectionDataContract屬性而且又不滿足集合要求那麼將拋出InvalidDataContractException而不是按照Serialized或ISerializable來處理
不能向實現了IXmlSerializable接口的類型使用CollectionDataContractAttribute屬性否則會拋出InvalidDataContractException向非集合應用CollectionDataContractAttribute屬性以及非字典集合指定KeyName或者ValueName屬性也都將拋出此異常
From:http://tw.wingwit.com/Article/program/net/201311/12431.html