他既是對與否的同時。
隨着諸如BinaryFormatter
這樣的事情,這是一個非問題;序列化流包含了完整的元數據類型,所以如果你有:
[Serializable] abstract class SomeBase {}
[Serializable] class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
和序列obj
,那麼它包含「我是SomeConcrete
」的流。這使得生活變得簡單,但冗長,特別是在重複時。它也很脆弱,因爲它在反序列化時需要相同的實現;對於不同的客戶端/服務器實現或長期存儲都不利。
與XmlSerializer
(我猜這個博客正在討論),沒有元數據 - 但元素名稱(或xsi:type
屬性)用於幫助識別使用哪個元數據。爲了這個工作,序列化程序需要事先知道什麼名字映射到哪些類型。
最簡單的方法是用我們知道的子類修飾基類。然後序列化程序可以檢查其中的每一個(以及任何其他特定於xml的屬性),以便發現當它看到<someConcreteType>
元素時,該元素映射到SomeConcrete
實例(請注意,名稱不需要匹配,因此它可以'只需按名稱查找它)。
[XmlInclude(typeof(SomeConcrete))]
public abstract class SomeBase {}
public class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
XmlSerializer ser = new XmlSerializer(typeof(SomeBase));
ser.Serialize(Console.Out, obj);
然而,如果他是一個純粹的(或數據不可用),則存在另一種;您可以通過重載構造函數將所有這些數據分別指定爲至XmlSerializer
。例如,您可以從配置(或者可能是一個IoC容器)中查找一組已知的子類型,並手動設置構造函數。這不是非常棘手,但它是非常棘手,它是不值得的,除非你實際上需要它。
public abstract class SomeBase { } // no [XmlInclude]
public class SomeConcrete : SomeBase { }
...
SomeBase obj = new SomeConcrete();
Type[] extras = {typeof(SomeConcrete)}; // from config
XmlSerializer ser = new XmlSerializer(typeof(SomeBase), extras);
ser.Serialize(Console.Out, obj);
此外,與XmlSerializer
如果你去自定義構造函數的路線,其緩存和重新使用XmlSerializer
實例是很重要的;否則每次使用都會加載一個新的動態程序集 - 非常昂貴(它們不能被卸載)。如果使用簡單的構造函數,它會緩存並重新使用該模型,因此只使用一個模型。
YAGNI指示我們應該選擇最簡單的選項;使用[XmlInclude]
可以消除對複雜構造函數的需求,並且無需擔心緩存序列化程序。另一種選擇是在那裏,並且完全支持。
重新您的後續問題:
通過「工廠模式」,他在談論您的代碼不知道SomeConcrete
的情況,可能是由於的IoC/DI或類似的框架;所以你可能有:
SomeBase obj = MyFactory.Create(typeof(SomeBase), someArgsMaybe);
哪個計算出相應的SomeBase
具體實施,其實例化並把它回來。顯然,如果我們的代碼不知道具體類型(因爲它們只在配置文件中指定),那麼我們不能使用XmlInclude
;但我們可以解析配置數據並使用ctor方法(如上所述)。實際上,大多數時候XmlSerializer
與POCO/DTO實體一起使用,所以這是人爲的關注。
和重新接口;同樣的事情,但更靈活(一個接口不需要類型層次結構)。但XmlSerializer
不支持此模式。坦率地說,艱難;這不是它的工作。它的工作是讓你存儲和傳輸數據。未執行。任何xml架構生成的實體都不會有方法。數據是具體的,而不是抽象的。只要你認爲「DTO」,界面辯論就不是問題。因無法在其邊界上使用界面而煩惱的人不會分心,即他們正在嘗試做的:
Client runtime entities <---transport---> Server runtime entities
,而不是限制較少
Client runtime entities <---> Client DTO <--- transport--->
Server DTO <---> Server runtime entities
現在,在許多(大多數?)情況下,DTO和實體可以是相同的;但是如果你想要做一些交通工具不喜歡的事情,那就引入一個DTO;不要與串行器對抗。同樣的道理,當人們都在努力寫自己的對象適用:
class Person {
public string AddressLine1 {get;set;}
public string AddressLine2 {get;set;}
}
爲形式的XML:
<person>
<address line1="..." line2="..."/>
</person>
如果你想要這個,intoduce對應於運輸DTO,和之間進行映射你的實體和DTO:
// (in a different namespace for the DTO stuff)
[XmlType("person"), XmlRoot("person")]
public class Person {
[XmlElement("address")]
public Address Address {get;set;}
}
public class Address {
[XmlAttribute("line1")] public string Line1 {get;set;}
[XmlAttribute("line2")] public string Line2 {get;set;}
}
這也適用於所有其他niggles,如:
- 爲什麼我需要無參數構造函數?
- 爲什麼我需要一個setter用於我的收藏屬性?
- 爲什麼我不能使用不可變類型?
- 爲什麼我的類型必須公開?
- 我該如何處理複雜的版本控制?
- 我該如何處理具有不同數據佈局的不同客戶端?
- 爲什麼我不能使用接口?
- 等,等
你並不總是有這些問題;但如果你這樣做 - 引入一個DTO(或幾個),你的問題就會消失。回顧關於接口的問題; DTO類型可能不是基於接口的,但是您的運行時/業務類型可以。
非常感謝你的信息。 – Natrium 2009-08-26 13:14:33