第七节:将类型序列化为不同的类型以及将类型反序列化为不同的对象

.NET Framework的序列化架构相当全面,本节要讨论如何设计一个类型,本节要讨论如何设计一个类型,它能将自己的序列化或反序列化成一个不同的类型或对象。下面列举一些有趣的例子。

1、         有的类型(比如System.DBNull和System.Reflection.Missing)设计成每个AppDomain一个实例。我们经常把这些类型称为单实例类型。给定一个DBNull对象引用,序列化和反序列化它不应造成在AppDomain中创建一个DBNull对象。序列化后,返回的引用应指向AppDomain中现有的DBNull对象。

2、         对于某些类型来说(比如System.Type,System.Reflection.Assembly和其他反射类型,比如MemberInfo),每个类型、每个程序集或每个成员等只有一个实例。例如,假定一个数组中的每个元素都引用一个MemberInfo对象,其中有5个元素都引用一个MemberInfo对象。在序列化和反序列化这个数组之后,当初引用一个MemberInfo对象的5个元素现在还是应该引用一个MemberInfo对象。除此之外,这些元素引用的那个MemberInfo对象还必须实际对应于AppDomain中的一个特定的成员。在轮询数据库连接对象或者其他任何类型的对象时,也可以利用这个单实例功能。

3、         对于远程控制的对象,CLR序列化与服务器对象有关的信息。在客户端上反序列化时,会造成CLR创建一个代理对象。这个代理对象的类型有别于服务器对象的类型,但这对于客户端代码来说是透明的。客户端直接在代理对象上调用实例方法。然后,代理代码内部会将调用远程发送给服务器,有后者实际执行请求的操作。

下面来看看一些示例代码,他们展示了如何正确序列化和反序列化一个单实例类型:

[Serializable]
    public sealed class Singleton : ISerializable
    {
        private static readonly Singleton s_theOneObject = new Singleton();
        public string Name = "Jeff";
        public DateTime date = DateTime.Now;

        //
        private Singleton() { }
        public static Singleton GetSingleton()
        {
            return s_theOneObject;
        }
        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.SetType(typeof(SingleSerializationHelper));
        }
        [Serializable]
        private sealed class SingleSerializationHelper : IObjectReference
        {
            //这个方法在对象反序列化之后调用
            public Object GetRealObject(StreamingContext context)
            {
                return Singleton.GetSingleton();
            }
        }
    }

Singleton类所代表的类型规定每个AppDomain只能存在它自己的一个实例。以下代码测试Singleton的序列化和反序列化代码,保证AppDomain中存在Singleton类型的一个实例:

  private static void SingletonSerializationTest()
        {
            Singleton[] a1 = { Singleton.GetSingleton(), Singleton.GetSingleton() };
            Console.WriteLine("do both elements refer to the same object ?" + (a1[0] == a1[1]));//true

            using (var stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, a1);
                stream.Position = 0;
                Singleton[] a2 = (Singleton[])formatter.Deserialize(stream);

                Console.WriteLine("do both elements refer to the same object ?" + (a2[0] == a2[1]));//true
                Console.WriteLine("do both elements refer to the same object ?" + (a2[0] == a2[0]));//true
            }
        }

现在,我们通过分析代码来理解所发生的事情。Singleton类型加载到AppDomain中时,CLR调用它的静态构造器,它构造一个对象,并将对象的引用保存到静态字段s_theOneObject中。Singleton类没有提供任何公共构造器,这防止了其他任何代码构造类的其他实例。

在SingletonSerializationTest中,我们创建了包含两个元素的一个数组;每个元素都引用Singleton对象。为了初始化两个元素,我们调用Singleton的静态GetSingleton方法。这个方法返回对一个Singleton对象的引用。对Console的WriteLine方法的第一个调用显示true,证明两个元素引用的是同一个对象。

现在SingletonSerializationTest调用格式化器的Serialize方法序列化数组及其元素。序列化第一个Singleton时,格式化器检测Singleton类型实现了ISerializable接口,并调用GetObjectData方法。这个方法调用SetType,向它传递SingleSerializationHelper类型,告诉格式化器将Singleton对象序列化成一个SingleSerializationHelper对象。由于AddValue没有调用,所以没有额外的字段信息写入流。由于格式化器自动检测出两个数组元素都引用一个对象,所以格式化器只能序列化一个对象。

序列化数组之后,SingletonSerializationTest调用格式化器的Deserialize方法。对流进行反序列化时,格式化器尝试反序列化一个SingleSerializationHelper对象,这是格式化器之前被“欺骗”所序列化的东西。构造好SingleSerializationHelper对象够,格式化器发现这个类型实现了IObjectReference接口,格式化器会调用GetRealObject方法。这个方法返回在对象反序列化好之后你整整想要引用的对象。在我的例子中,SingleSerializationHelper类型让GetRealObject返回对AppDomain中已存在的Singleton中已存在的Singleton对象的一个引用。所以,当格式化器的Deserialize方法返回时,a2数组包含两个元素,两者都引用Appdomin的Singleton对象。

原文地址:https://www.cnblogs.com/bingbinggui/p/4621872.html