共用方式為


序列化方針

本文件列出在設計要序列化的 API 時所要考量的指導方針。

.NET Framework 提供已針對多個序列化情節最佳化的三個主要序列化技術。 下表列出這些技術以及與這些技術相關的主要 Framework 型別。

技術

相關類別

備註

資料合約序列化

DataContractAttribute

DataMemberAttribute

DataContractSerializer

NetDataContractSerializer

DataContractJsonSerializer

ISerializable

一般持續性

Web 服務

JSON

XML 序列化

XmlSerializer

XML 格式 (具有完全控制)

執行階段 - 序列化 (二進位和 SOAP)

SerializableAttribute

ISerializable

BinaryFormatter

SoapFormatter

.NET 遠端處理

當您設計新的型別時,您應該決定這些型別必須支援哪些技術 (如果有的話)。 下列指導方針說明如何做出這項決定以及如何提供這類支援。 這些指導方針並不是為了幫助您選擇應該在應用程式或程式庫的實作中使用哪一種序列化技術。 這類指導方針與 API 設計沒有直接的關聯性,所以不在本主題的討論範圍內。

方針

  • 當您設計新的型別時,請務必考量序列化。

    對任何型別而言,序列化都是重要的設計考量,因為程式可能需要保存或傳輸該型別的執行個體。

6exf3h2k.collapse_all(zh-tw,VS.120).gif選擇要支援的正確序列化技術

任何給定型別都可以支援零個、一個或多個序列化技術。

  • 如果型別的執行個體可能需要在 Web 服務中保存或使用,請考慮支援「資料合約序列化」(Data Contract Serialization)。

  • 如果您需要針對序列化型別時所產生的 XML 格式擁有更大的控制權,請考慮支援「XML 序列化」(XML Serialization) 來取代資料合約序列化,或是兩者都支援。

    在您需要使用資料合約序列化所不支援的 XML 建構的某些互通性情況下 (例如,為了產生 XML 屬性),就可能需要這樣的處理方式。

  • 如果型別的執行個體需要橫跨 .NET 遠端處理界限,請考慮支援「執行階段序列化」(Runtime Serialization)。

  • 請避免針對一般持續性理由來支援執行階段序列化或 XML 序列化。 請改用資料合約序列化。

6exf3h2k.collapse_all(zh-tw,VS.120).gif支援資料合約序列化

型別可以藉由將 DataContractAttribute 套用至型別並將 DataMemberAttribute 套用至型別的成員 (欄位和屬性) 來支援資料合約序列化。

<DataContract()> Public Class Person
    <DataMember()> Public Property LastName As String
    <DataMember()> Public Property FirstName As String

    Public Sub New(ByVal firstNameValue As String, ByVal lastNameValue As String)
        FirstName = firstNameValue
        LastName = lastNameValue
    End Sub

End Class
    [DataContract]
    class Person
    {

        [DataMember]
        string LastName { get; set; }
        [DataMember]
        string FirstName { get; set; }

        public Person(string firstNameValue, string lastNameValue)
        {
            FirstName = firstNameValue;
            LastName = lastNameValue;
        }
    }

  1. 如果可以在部分信任中使用型別,請考慮將型別的資料成員標記為 public。 在完全信任中,資料合約序列化程式可以序列化及還原序列化非 public 型別和成員,但是只有 public 成員可以在部分信任中序列化及還原序列化。

  2. 請針對具有 Data-MemberAttribute 的所有屬性實作 getter 和 setter。 資料合約序列化程式需要 getter 和 setter,才能考慮將此型別序列化。 如果此型別不會在部分信任中使用,則其中一個或兩個屬性存取子可以不是 public。

    <DataContract()> Class Person2
        Private lastNameValue As String
        Private firstNameValue As String
    
        Public Sub New(ByVal firstName As String, ByVal lastName As String)
            Me.lastNameValue = lastName
            Me.firstNameValue = firstName
        End Sub
    
        <DataMember()> Property LastName As String
            Get
                Return lastNameValue
            End Get
    
            Set(ByVal value As String)
                lastNameValue = value
            End Set
    
        End Property
    
        <DataMember()> Property FirstName As String
            Get
                Return firstNameValue
    
            End Get
            Set(ByVal value As String)
                firstNameValue = value
            End Set
        End Property
    
    End Class
    
    [DataContract]
    class Person2
    {
    
        string lastName;
        string firstName;
    
        public Person2(string firstName, string lastName)
        {
            this.lastName = lastName;
            this.firstName = firstName;
        }
    
        [DataMember]
        public string LastName
        { 
            // Implement get and set.
            get { return lastName; }
            private set { lastName = value; }
    
        }
    
        [DataMember]
        public string FirstName
        {
            // Implement get and set.
            get { return firstName; }
            private set { firstName = value; }
        }
    }
    
  3. 請考慮針對還原序列化之執行個體的初始化使用序列化回呼。

    當還原序列化物件時,不會呼叫建構函式。 因此,正常建構期間所執行的任何邏輯都需要實作為其中一個「序列化回呼」(Serialization Callback)。

    <DataContract()> _
    Class Person3
        <DataMember()> Private lastNameValue As String
        <DataMember()> Private firstNameValue As String
        Dim fullNameValue As String
    
        Public Sub New(ByVal firstName As String, ByVal lastName As String)
            lastNameValue = lastName
            firstNameValue = firstName
            fullNameValue = firstName & " " & lastName
        End Sub
    
        Public ReadOnly Property FullName As String
            Get
                Return fullNameValue
            End Get
        End Property
    
        <OnDeserialized()> Sub OnDeserialized(ByVal context As StreamingContext)
            fullNameValue = firstNameValue & " " & lastNameValue
        End Sub
    End Class
    
    [DataContract]
    class Person3
    {
        [DataMember]
        string lastName;
        [DataMember]
        string firstName;
        string fullName;
    
        public Person3(string firstName, string lastName)
        {
            // This constructor is not called during deserialization.
            this.lastName = lastName;
            this.firstName = firstName;
            fullName = firstName + " " + lastName;
        }
    
        public string FullName
        {
            get { return fullName; }
        }
    
        // This method is called after the object 
        // is completely deserialized. Use it instead of the
        // constructror.
        [OnDeserialized] 
        void OnDeserialized(StreamingContext context)
        {
            fullName = firstName + " " + lastName;
        }
    }
    

    OnDeserializedAttribute 屬性是最常用的回呼屬性。 此系列中的其他屬性為 OnDeserializingAttribute、OnSeralizingAttribute 和 OnSerializedAttribute。 這些屬性可用來分別標記在還原序列化之前、序列化之前以及最後在序列化之後所執行的回呼。

  4. 當您還原序列化複雜物件圖形時,請考慮使用 KnownTypeAttribute 來指示應該使用的具象型別。

    例如,如果還原序列化之資料成員的某個型別以抽象類別來表示,則序列化程式將需要「已知型別」(Known Type) 資訊來決定哪一個具象型別要執行個體化及指派給成員。 如果已知型別不是使用此屬性所指定,它需要明確傳遞給序列化程式 (您可以將已知型別傳遞給序列化程式建構函式來進行這項處理),或者需要在組態檔中指定已知型別。

    <KnownType(GetType(USAddress)), _
    DataContract()> Class Person4
        <DataMember()> Property fullNameValue As String
        <DataMember()> Property addressValue As USAddress ' Address is abstract
    
        Public Sub New(ByVal fullName As String, ByVal address As Address)
            fullNameValue = fullName
            addressValue = address
        End Sub
    
        Public ReadOnly Property FullName() As String
            Get
                Return fullNameValue
            End Get
    
        End Property
    End Class
    
    <DataContract()> Public MustInherit Class Address
        Public MustOverride Function FullAddress() As String
    End Class
    
    <DataContract()> Public Class USAddress
        Inherits Address
        <DataMember()> Public Property Street As String
        <DataMember()> Public City As String
        <DataMember()> Public State As String
        <DataMember()> Public ZipCode As String
    
        Public Overrides Function FullAddress() As String
            Return Street & "\n" & City & ", " & State & " " & ZipCode
        End Function
    End Class
    
    // The KnownTypeAttribute specifies types to be
    // used during serialization.
    [KnownType(typeof(USAddress))]
    [DataContract]
    class Person4
    {
    
        [DataMember]
        string fullNameValue;
        [DataMember]
        Address address; // Address is abstract
    
        public Person4(string fullName, Address address)
        {
            this.fullNameValue = fullName;
            this.address = address;
        }
    
        public string FullName
        {
            get { return fullNameValue; }
        }
    }
    
    [DataContract]
    public abstract class Address
    {
        public abstract string FullAddress { get; }
    }
    
    [DataContract]
    public class USAddress : Address
    {
    
        [DataMember]
        public string Street { get; set; }
        [DataMember]
        public string City { get; set; }
        [DataMember]
        public string State { get; set; }
        [DataMember]
        public string ZipCode { get; set; }
    
        public override string FullAddress
        {
            get
            {
                return Street + "\n" + City + ", " + State + " " + ZipCode;
            }
        }
    }
    

    如果已知型別的清單無法以靜態方式得知 (當編譯 [Person] 類別時),KnownTypeAttribute 也可指向一個方法,此方法會在執行階段傳回已知型別的清單。

  5. 當建立或變更可序列化的型別時,請務必考慮回溯相容性與向前相容性。

    請牢記,型別之未來版本的序列化資料流可以還原序列化成該型別的目前版本,反之亦然。 請務必了解資料成員 (即使是 private 和內部成員) 在型別的未來版本中無法變更其名稱、型別或甚至是順序,除非採取特別的步驟,利用資料合約屬性的明確參數來保留合約。在變更可序列化的型別時,請測試序列化的相容性。 請嘗試將新的版本還原序列化成舊的版本,反之亦然。

  6. 請考慮實作 IExtensibleDataObject 介面來允許在型別的不同版本之間往返。

    此介面可讓序列化程式確保往返期間不會有任何資料遺失。 ExtensionData 屬性會儲存來自目前版本未知之型別的未來版本中的任何資料。 當目前版本接著序列化並還原序列化成未來版本時,其他資料將會透過 ExtensionData 屬性值在序列化資料流中提供。

    <DataContract()> Class Person5
        Implements IExtensibleDataObject
        <DataMember()> Dim fullNameValue As String
    
        Public Sub New(ByVal fullName As String)
            fullName = fullName
        End Sub
    
        Public ReadOnly Property FullName
            Get
                Return fullNameValue
            End Get
        End Property
        Private serializationData As ExtensionDataObject
        Public Property ExtensionData As ExtensionDataObject Implements IExtensibleDataObject.ExtensionData
            Get
                Return serializationData
            End Get
            Set(ByVal value As ExtensionDataObject)
                serializationData = value
            End Set
        End Property
    End Class
    
    // Implement the IExtensibleDataObject interface.
    [DataContract]
    class Person5 : IExtensibleDataObject
    {
        ExtensionDataObject serializationData;
        [DataMember]
        string fullNameValue;
    
        public Person5(string fullName)
        {
            this.fullNameValue = fullName;
        }
    
        public string FullName
        {
            get { return fullNameValue; }
        }
    
        ExtensionDataObject IExtensibleDataObject.ExtensionData
        {
            get 
            {                 
                return serializationData; 
            }
            set { serializationData = value; }
        }
    }
    

    如需詳細資訊,請參閱向前相容資料合約

6exf3h2k.collapse_all(zh-tw,VS.120).gif支援 XML 序列化

資料合約序列化是 .NET Framework 中的主要 (預設) 序列化技術,但是資料合約序列化不支援一些序列化情節。 例如,它無法讓您完全控制序列化程式所產生或使用之 XML 的形狀。 如果需要這樣的精確控制,必須使用「XML 序列化」(XML Serialization),而且您需要設計您的型別來支援這項序列化技術。

  1. 請避免專門為了 XML 序列化來設計型別,除非您有非常強烈的理由為了控制所產生之 XML 的形狀。 這項序列化技術已經由前一節所討論的資料合約序列化所取代。

    換句話說,請勿將 System.Runtime.Serialization 命名空間中的屬性套用至新的型別,除非您知道此型別將會搭配 XML 序列化使用。 下列範例示範如何使用 System.Xml.Serialization 來控制所產生之 XML 的形狀。

    Public Class Address2
        ' Supports XML Serialization.
        <System.Xml.Serialization.XmlAttribute()> _
        Public ReadOnly Property Name As String ' Serialize as XML attribute, instead of an element.
            Get
                Return "Poe, Toni"
            End Get
        End Property
        <System.Xml.Serialization.XmlElement(ElementName:="StreetLine")> _
        Public Street As String = "1 Main Street"  ' Explicitly names the element 'StreetLine'.
    End Class
    
    public class Address2
    {
        [System.Xml.Serialization.XmlAttribute] // Serialize as XML attribute, instead of an element.
        public string Name { get { return "Poe, Toni"; } set { } }
        [System.Xml.Serialization.XmlElement(ElementName = "StreetLine")] // Explicitly name the element.
        public string Street = "1 Main Street";        
    }
    
  2. 如果您希望對序列化 XML 的形狀有更大的控制權,而不是套用 XML 序列化屬性來使用提供的控制權,請考慮實作 IXmlSerializable 介面。 此介面的兩個方法 ReadXml 和 WriteXml 可讓您完全控制序列化的 XML 資料流。 您也可以藉由套用 XmlSchemaProviderAttribute 屬性來控制為此型別產生的 XML 結構描述。

6exf3h2k.collapse_all(zh-tw,VS.120).gif支援執行階段序列化

「執行階段序列化」(Runtime Serialization) 是 .NET 遠端處理所使用的技術。 如果您認為您的型別將會使用 .NET 遠端處理來傳輸,則需要確定型別有支援執行階段序列化。

「執行階段序列化」(Runtime Serialization) 的基本支援可以藉由套用 SerializableAttribute 屬性來提供,而更進階的情節則牽涉到實作簡單的「執行階段可序列化模式」(Runtime Serializable Pattern) (實作 ISerializable 並提供序列化建構函式)。

  1. 如果您的型別將會搭配 .NET 遠端處理使用,請考慮支援執行階段序列化。 例如,System.AddIn 命名空間會使用 .NET 遠端處理,所以在 System.AddIn 增益集之間交換的所有型別都需要支援執行階段序列化。

    <Serializable()> Public Class Person6 ' Support runtime serialization with the SerializableAttribute.
    
        ' Code not shown.
    End Class
    
    // Apply SerializableAttribute to support runtime serialization.
    [Serializable]
    public class Person6 
    {
        // Code not shown.
    }
    
  2. 如果您想要對序列化處理序擁有完整的控制權,請考慮實作「執行階段可序列化模式」(Runtime Serializable Pattern)。 例如,如果您想要在資料序列化或還原序列化時加以轉換。

    此模式非常簡單。 您只需要實作 ISerializable 介面,並提供還原序列化物件時使用的特殊建構函式。

        ' Implement the ISerializable interface for more control.
        <Serializable()> Public Class Person_Runtime_Serializable
            Implements ISerializable
    
            Private fullNameValue As String
    
            Public Sub New()
                ' empty constructor.
            End Sub
            Protected Sub New(ByVal info As SerializationInfo, _
                              ByVal context As StreamingContext)
                If info Is Nothing Then
                    Throw New System.ArgumentNullException("info")
                    FullName = CType(info.GetValue("name", GetType(String)), String)
                End If
            End Sub
    
            Private Sub GetObjectData(ByVal info As SerializationInfo, _
                                      ByVal context As StreamingContext) _
                                  Implements ISerializable.GetObjectData
                If info Is Nothing Then
                    Throw New System.ArgumentNullException("info")
                    info.AddValue("name", FullName)
                End If
            End Sub
    
            Public Property FullName As String
    
                Get
                    Return fullNameValue
                End Get
                Set(ByVal value As String)
                    fullNameValue = value
    
                End Set
            End Property
    
        End Class
    
    
    // Implement the ISerializable interface for more control.
    [Serializable]
    public class Person_Runtime_Serializable : ISerializable
    {
        string fullName;
    
        public Person_Runtime_Serializable() { }
        protected Person_Runtime_Serializable(SerializationInfo info, StreamingContext context){
            if (info == null) throw new System.ArgumentNullException("info");
            fullName = (string)info.GetValue("name", typeof(string));
        }
        [SecurityPermission(SecurityAction.LinkDemand,
        Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info,
                StreamingContext context) {
            if (info == null) throw new System.ArgumentNullException("info");
            info.AddValue("name", fullName);
        }
    
        public string FullName
        {
            get { return fullName; }
            set { fullName = value; }
        }
    }
    
  3. 請讓序列化建構函式處於受保護狀態,並提供兩個參數,這兩個參數的型別與名稱與此處範例所顯示的一模一樣。

    Protected Sub New(ByVal info As SerializationInfo, _
                      ByVal context As StreamingContext)
    
    protected Person_Runtime_Serializable(SerializationInfo info, StreamingContext context){
    
  4. 請務必明確實作 ISerializable 成員。

    Private Sub GetObjectData(ByVal info As SerializationInfo, _
                              ByVal context As StreamingContext) _
                          Implements ISerializable.GetObjectData
    
    void ISerializable.GetObjectData(SerializationInfo info,
            StreamingContext context) {
    
  5. 請務必將連結需求套用至 ISerializable.GetObjectData 實作。 如此可確保只有完全受信任的核心和執行階段序列化程式可存取此成員。

    <Serializable()> Public Class Person_Runtime_Serializable2
        Implements ISerializable
        <SecurityPermission(SecurityAction.LinkDemand, Flags:=SecurityPermissionFlag.SerializationFormatter)> _
        Private Sub GetObjectData(ByVal info As System.Runtime.Serialization.SerializationInfo, _
                                 ByVal context As System.Runtime.Serialization.StreamingContext) _
                             Implements System.Runtime.Serialization.ISerializable.GetObjectData
            ' Code not shown.
        End Sub
    End Class
    
    [SecurityPermission(SecurityAction.LinkDemand,
    Flags = SecurityPermissionFlag.SerializationFormatter)]
    

請參閱

概念

使用資料合約

資料合約序列化程式支援的型別

安全性和序列化

其他資源

資料合約序列化程式

二進位序列化

Remote Objects

XML 和 SOAP 序列化