8.10 序列化(第二版)

8.10 序列化(第二版)

  • .NET框架提供了三种主要的序列化技术,如下:

    • 数据协定(Data Contract) 序列化;
    • XML 序列化;
    • 运行时 序列化(二进制和SOAP)

image

  • DO​:在设计新的类型时考虑到序列化。

    如果类型实例需要持久化,或需要传输,则设计时要考虑如何序列化。

1 选择要支持的序列化技术

  • DO​:优先支持 数据协定 序列化。

  • CONSIDER​:如果需要 控制序列化方式 ,可以只支持XML序列化,或同时支持数据协定序列化和XML序列化。

  • CONSIDER​:如果需要跨.NET Remoting的边界传输类型,应实现 运行时序列化

2 对数据协定序列化的支持

  • CONSIDER​:如果类型会被用于部分可信(partial trust)环境,序列化成员要定义为 公有

    • 完全可信(full trust)环境下,Data Contract可以对所有成员进行序列化;部分可信环境下,仅对公有成员进行序列化。

  • DO ​:需要序列化的属性 必须有 getter和setter。

    • 如果仅用于完全可信的环境中,getter和setter可以是非公有的。

  • DO​:使用序列化回调函数在反序列化时初始化实例。
    序列化回调函数通过Attribute标注,包括:

    • OnDeserializedAttribute​​ 反序列化后 触发
    • OnDeserializingAttribute​​ 反序列化前 触发
    • OnSerializingAttribute​​ 序列化前 触发
    • OnSerializedAttribute​​ 序列化后 触发

    我们可以在这些回调函数中进行一些附加操作

    • [DataContract]
      class Person {
          [DataMember] string lastName;
          [DataMember] string firstName;
          string fullName;
        
          public Person(string firstName, string lastName) {
              this.lastName = lastName;
              this.firstName = firstName;
              fullName = firstName + " " + lastName;
          }
        
          public string FullName => fullName;
        
          [OnDeserialized]
          void OnDeserialized(StreamingContext context) {
              fullName = firstName + " " + lastName;
          }
      }
      

  • CONSIDER​:使用 KnowTypeAttribute ​标注反序列化时会遇到的具体类型。

    KnowTypeAttribute​ 有两个构造函数,KnownTypeAttribute(Type type)​ 传入类型,KnownTypeAttribute(string methodName)​ 传入返回Type[]​ 的静态方法:

    [KnownType("GetKnownTypes")]
    [DataContract]
    public class Person {
        ...
        public static Type[] GetKnownTypes() {
            return new[] { typeof(DateTime, School, ... ) };
        }
    }
    

  • DO​:创建、改变序列化类型时要考虑向后、向前兼容性。

    Data Contract反序列化与以下因素相关:

    • 数据成员的名字
    • 类型
    • 顺序

    改变这些内容会导致反序列化失败。如果要修改,应进行特殊处理(可以通过DataMember的可选参数进行标注)。

  • CONSIDER​:可以通过实现 IExtensibleDataObject 支持新老版本的双向转换。

    [DataContract]
    class Person : IExtensibleDataObject {
        ...
        ExtensionDataObject serializationData;
        ExtensionDataObject IExtensibleDataObject.ExtensionData {
            get => serializationData;
            set => serializationData = value;
        }
    }
    

3 对XML序列化的支持

DataContract 是.NET框架中主要(默认)序列化技术,但也存在一些不支持的场景(如多维数组)。此时可以使用XML序列化,我们可以通过它完全控制序列化过程。

  • AVOID​:除非有强烈的理由控制输出的内容,否则不要使用 XML序列化

    我们可以通过特性对生成的XML加以控制:

    public class Address {
        [XmlAttribute]  // 序列化时作为XML属性,而非元素
        public string Name { get { return "John Smith";} set { } }
      
        [XmlElement(ElementName = "StreetLine")]  // 指明元素名称
        public string Street = "1 Some Street";
    }
    

  • CONSIDER​:应用XML序列化Attribute后,如果生成的XML内容仍不满足要求,可以实现 IXmlSerializable 接口。

    该接口有两个方法,ReadXml​和WriteXml​,可以用它们完全控制生成的XML内容。如果上述接口还不够,可以给类型添加XmlSchemaProviderAttribute​,用于对XML架构(XML schema)进行控制。

4 对运行时序列化的支持

  • CONSIDER​:如果类型会被用于.NET Remoting,考虑支持 运行时序列化

    例如:System.AddIn用到了.NET Remoting,因此System.AddIn的外接程序之间传输的所有类型都必须支持运行时序列化。

    [Serializable]  // 通过添加该特性便可支持运行时序列化。
    public class Person {
        ...
    }
    

  • CONSIDER​:如果想要完全控制序列化的整个过程,实现 ISerializable 接口。

    该接口包含一个ISerializable.GetObjectData​方法,用于序列化,我们还需定义一个构造函数,用于反序列化。该构造函数应定义为protected,如果是密封类型,则是private。
    接口方法要显式实现,并标注SecurityAction.LinkDemand​,保证只有完全可信的代码和运行时序列化程序才能访问该成员;构造函数要定义为protected,有两个参数。
    具体实现参见示例代码。

    [Serializable]
    public class Person : ISerializable {
        string fullName;
      
        public string FullName {
            get => fullName;
            set => fullName = value;
        }
      
        public Person() { }
        // 反序列化构造函数
        protected Person(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);
        }
    }
    

posted @   hihaojie  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
点击右上角即可分享
微信分享提示