資料合約已知型別
KnownTypeAttribute 類別可讓您預先指定在還原序列化期間應該納入考量的型別。如需實用範例,請參閱Known Types範例。
一般來說,當您在用戶端與服務之間傳送參數並傳回值時,兩邊的端點都會共用要傳送之資料的所有資料合約。但是,這種現象在下列情況中不會出現:
- 傳送的資料合約是由預期的資料合約衍生而來 (如需詳細資訊,請參閱 資料合約等價中有關繼承的小節說明)。在此情況下,所傳送資料的資料合約與接收的端點所預期的資料合約不會一樣。
- 要傳送的資訊宣告型別是一種介面,而不是類別、結構或列舉。因此,您無法預先得知實際傳送了哪種可實作介面的型別,也因此接收的端點無法預先判斷已傳送資料的資料合約。
- 要傳送的資訊宣告型別為 Object。由於每種型別都繼承自 Object,而且您無法預先得知實際傳送的型別,因此接收的端點無法預先判斷已傳送資料的資料合約。以下是第一個項目的特殊情況:每個資料合約都衍生自預設、且針對 Object 產生的空白資料合約。
- 有些型別 (包括 .NET Framework 型別) 的成員隸屬於先前三大類別的其中一個類別。例如,Hashtable 會透過 Object 將實際物件儲存到雜湊資料表中。在序列化這些型別時,接收的一方無法預先判斷這些成員的資料合約。
KnownTypeAttribute 類別
當資料抵達接收的端點時,WCF 執行階段會嘗試將資料還原序列化為 Common Language Runtime (CLR) 型別的執行個體。還原系列化作業所產生的型別,首先會經由檢查傳入訊息來判斷訊息內容所符合的資料合約來加以選定。接著,還原序列化引擎會嘗試尋找可實作資料合約 (相容於訊息內容) 的 CLR 型別。在此處理序中,我們會將還原序列化引擎所允許的候選型別集合稱為還原序列化程式的「已知型別」集合。
讓還原序列化引擎識別型別的一種方式,就是使用 KnownTypeAttribute。屬性無法套用至個別資料成員,只能套用至整個資料合約型別。屬性會套用至可以是類別或結構的「外部型別」(Outer Type)。在屬性最基本的用途當中,套用屬性會將型別指定為「已知型別」。這樣一來,每當外部型別的物件或是透過其成員參照的任何物件進行還原序列化,已知型別就會變成已知型別集合的一部分。超過一個以上的 KnownTypeAttribute 屬性可以套用至相同型別中。
已知型別與基本型別
基本型別,以及被視為基本型別的特性型別 (例如,DateTime 和 XmlElement) 將一律具有「已知」狀態,而且一律不需要透過此機制來新增。但是,您需要明確地新增基本型別陣列。大部分的集合都會被視為與陣列相等 (非泛型集合將被視為與 Object 陣列相等)。如需使用基本型別、基本型別陣列,與基本型別集合的範例,請參閱範例 4。
注意: |
---|
與其他基本型別不同的是,DateTimeOffset 結構預設並不是已知型別,所以必須手動新增至已知型別清單中。 |
範例
下列範例說明使用中的 KnownTypeAttribute 類別。
範例 1
具有繼承關係的類別共有三個。
如果 ShapeOfLogo
成員已設為 CircleType
或 TriangleType
物件,由於還原序列化引擎無法識別任何具有資料合約名稱 "Circle" 或 "Triangle" 的型別,您將可以序列化下列 CompanyLogo
類別,但是無法加以還原序列化。
下列程式碼說明如何正確撰寫 CompanyLogo
型別。
每次在還原序列化外部型別 CompanyLogo2
時,還原序列化引擎能夠識別 CircleType
和 TriangleType
,因此能夠為 "Circle" 和 "Triangle" 資料找尋相符的型別。
範例 2
在下列範例中,即使 CustomerTypeA
和 CustomerTypeB
同時具有 Customer
資料合約,每當還原序列化 PurchaseOrder
時,還是會建立 CustomerTypeB
執行個體,因為還原序列化引擎只能識別 CustomerTypeB
。
範例 3
在下列範例中,Hashtable 會將其內容當成 Object 儲存在內部。為了成功還原序列化雜湊資料表,還原序列化引擎必須能夠識別可在該處發生的可能型別集合。在此情況下,我們預先知道只有 Book
和 Magazine
物件會儲存在 Catalog
中,因此會透過 KnownTypeAttribute 屬性來加以新增。
範例 4
在下列範例中,資料合約會儲存數字與用來執行數字的運算式。Numbers
資料成員可以是整數、整數陣列,或是包含整數的 List。
下列為應用程式程式碼。
已知型別、繼承,與介面
當已知型別透過 KnownTypeAttribute 屬性關聯至特定型別時,已知型別同時已經和該型別的所有衍生型別產生關聯。例如,請參閱下列程式碼。
DoubleDrawing
類別不會要求 KnownTypeAttribute 屬性使用 AdditionalShape
欄位中的 Square
和 Circle
,因為基底類別 (Drawing
) 已經套用了這些屬性。
已知型別只可和類別與結構,而不能與介面產生關聯。
使用開放式泛型方法的已知型別
您可能需要將泛型型別新增為已知型別。但是,開放式泛型型別無法當成 KnownTypeAttribute 屬性的參數來傳送。
您可以透過替代機制來解決這個問題:撰寫可傳回型別清單,以便新增至已知型別集合的方法。接著,因為某些限制因素,可將方法名稱指定為 KnownTypeAttribute 屬性的字串引數。
方法必須存在於套用 KnownTypeAttribute 屬性的型別上、必須是靜態的、絕對不得接受任何參數,而且必須傳回可指派給 Type 之 IEnumerable 的物件。
您無法將 KnownTypeAttribute 屬性與方法名稱結合,也無法將 KnownTypeAttribute 屬性與相同型別上的實際型別結合。此外,您無法將一個以上包含方法名稱的 KnownTypeAttribute 套用至相同型別中。
請參閱下列類別。
theDrawing
欄位包含泛型類別 ColorDrawing
和泛型類別 BlackAndWhiteDrawing
的執行個體,兩者都繼承自泛型類別 Drawing
。一般來說,兩者必須同時新增至已知型別中,但是下列不是有效的屬性語法。
// Invalid syntax for attributes:
// [KnownType(typeof(ColorDrawing<T>))]
// [KnownType(typeof(BlackAndWhiteDrawing<T>))]
' Invalid syntax for attributes:
' <KnownType(GetType(ColorDrawing(Of T))), _
' KnownType(GetType(BlackAndWhiteDrawing(Of T)))>
因此,您必須建立方法來傳回這些型別。下列程式碼將接著說明撰寫此型別的正確方式。
新增已知型別的其他方式
您也可以將型別新增至 ReadOnlyCollection,並透過 DataContractSerializer 的 KnownTypes 屬性來加以存取。
另外,您可以透過組態檔來新增已知型別。當您並未控制進行適當還原序列化作業時需要已知型別的型別時,這個方法就會很有用,例如當您使用協力廠商的型別程式庫來搭配 Windows Communication Foundation (WCF) 時。
下列組態檔示範如何在組態檔中指定已知的類別。
<configuration>
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="MyCompany.Library.Shape,
MyAssembly, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=XXXXXX, processorArchitecture=MSIL">
<knownType type="MyCompany.Library.Circle,
MyAssembly, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=XXXXXX, processorArchitecture=MSIL"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
</configuration>
在先前的組態檔中,會宣告稱為 MyCompany.Library.Shape
的資料合約類別,而讓 MyCompany.Library.Circle
做為已知的類別。
請參閱
參考
KnownTypeAttribute
Hashtable
Object
DataContractSerializer
KnownTypes