zoukankan      html  css  js  c++  java
  • 运行时序列化

     一、序列化与反序列化

    序列化是将对象图转换成字节流的过程,反序列化是将字节流转换回对象图的过程

        class Program
        {
            static void Main(string[] args)
            {
                List<string> objectGraph = new List<string>() {"关羽", "吕蒙"};
                Stream stream = SerialzeToMemory(objectGraph);
    
                stream.Position = 0;//反序列化前,定位到内存流的起始位置
                objectGraph = (List<string>)DeserialzeFromMemory(stream);
                foreach (var obj in objectGraph)
                {
                    Console.WriteLine(obj);
                }
                Console.ReadKey();
            }
    
    
    
            private static MemoryStream SerialzeToMemory(object objectGraph)
            {
                //构造流来容纳序列化的对象
                MemoryStream stream = new MemoryStream();
    
                //构造序列化格式化器来执行所有真正的工作
                BinaryFormatter formatter = new BinaryFormatter();
    
                //告诉格式化器将对象序列化到流中
                formatter.Serialize(stream, objectGraph);
    
                return stream;
            }
    
            private static object DeserialzeFromMemory(Stream stream)
            {
                //构造序列化格式化器来做所有真正的工作
                BinaryFormatter formatter = new BinaryFormatter();
    
                //告诉格式化器从流中反序列化对象
                return formatter.Deserialize(stream);
            }
    
        }

    注意事项:

    ①保证代码为序列化和反序列化使用相同的格式化器
    ②可将多个对象图序列化到一个流中

            private static List<Customer> s_customers = new List<Customer>();
            private static List<Order> s_pendingOrders = new List<Order>();
            private static List<Order> s_processedOrders = new List<Order>();
    
            static void Main(string[] args)
            {
                //将多个对象图序列化一个流中
                MemoryStream stream = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, s_customers);
                formatter.Serialize(stream, s_pendingOrders);
                formatter.Serialize(stream, s_processedOrders);
    
                //反序列化应用程序的完整状态(和序列化时的顺序一样)
                s_customers = (List<Customer>) formatter.Deserialize(stream);
                s_pendingOrders = (List<Order>) formatter.Deserialize(stream);
                s_processedOrders = (List<Order>) formatter.Deserialize(stream);
    
                Console.ReadKey();
            }

    ③序列化对象时,类型的全名和类型定义程序集的全名会被写入流。BinaryFormatter默认输出程序集的完整标识,其中包含程序集的文件名(无扩展名)、版本号、语言文化以及公钥信息。反序列化对象时,格式化器首先获取程序集标识信息,并通过调用System.Reflection.Assembly的Load方法确保程序集已加载到正在执行的AppDoamin中。程序集加载好之后,格式化器在程序集中查找与要反序列化的对象匹配类型。找不到匹配类型则抛出异常,不再对更多的对象进行序列化,找到匹配的类型,就创建类型的实例,并用流中包含的值对其字段进行初始化。如果类型中的字段与流中读取的字段名不完全匹配,就抛出SerializetionException异常,不再对更多的对象进行序列化

     二、使类型可序列化

    1,必须在需要序列化的类型上加上System.SerializebleAttibute特性
    2,SerializebleAttibute这个定制特性只能应用与引用类型(class)、值类型(struct)、枚举类型(enum)和委托类型(delegate)。注意类型和委托总是可序列化的,所以不必显示应用SerializebleAttibute特性。
    3,SerializebleAttibute特性不会被派生类继承
    5,System.Object应用了SerializebleAttibute特性
    6,序列化会读取对象的所有字段,不管这些字段声明为public、protected、internal还是private

    三、控制序列化和反序列化

    [Serializable]
        public sealed class Circle
        {
            public double m_radius=10;//半径
    
            [NonSerialized]//当前字段不会被序列化(反序列化时,此值为0)
            public double m_area=10;//面积
    
    
    
            [OnDeserializing]
            private void OnDeserializing(StreamingContext context)
            {
                m_area = 20;//举例:在这个类型新版本中
            }
    
            [OnDeserialized]
            private void OnDeserialized(StreamingContext context)
            {
                m_area = 10;//举例:根据字段值初始化瞬时状态
            }
    
            [OnSerializing]
            private void OnSerializing(StreamingContext context)
            {
                m_area = 10;//举例:在序列化前,修改任何需要修改的状态
            }
            [OnSerialized]
            private void OnSerialized(StreamingContext context)
            {
                m_area = 10;//举例:在序列化后,恢复任何需要恢复的状态
            }
    
        }

    ①定义的方法必须获取一个StreamingContext参数,并返回void。方法名称可随意命名
    ②调用顺序:序列化一组对象时先调用标记了OnSerializing的方法,然后调用标记了OnSerialized特性的方法。反序列化时先调用标记了OnDeserializing的方法,然后调用标记了OnDeserialized的方法

    如果序列化类型的实例,在类型中添加新字段,然后视图反序列化不包含新字段的对象,格式化器会抛出SerializationExcption异常。可在新增字段中加上OptionalFieldAttribute特性

    四、格式化器如何序列化类型实例

    FCL在System.Runtime.Serialization命名空间提供了一个FormatterServices类型,该类型只包含静态方法,而且该类型不能实例化

    1,格式化器如何自动序列化类型引用了SerializableAttribute特性的对象

    ①格式化器调用FormatterServices的GetSerializableMembers方法。

    public static MemberInfo[] GetSerializableMembers(Type type, StreamingContext context);

    这个方法利用反射获取类型的public和private实例字段(标记了NonSerializedAttribute特性的字段除外)。方法返回由MemberInfo对象构成的数组,其中每个元素都对应一个可序列化的实例字段

    ②对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices的静态方法GetObjectData

    public static object[] GetObjectData(object obj, MemberInfo[] members);

    这个方法返回一个Object数组,其中每个元素都标识了被序列化的那个对象的一个字段的值。object数组中的元素0是MemberInfo数组中的元素0所标识的那个成员的值

    ③格式化器将程序集标识和类型的完整名称写入流中

    ④格式化器然后遍历两个数组中的元素,将每个成员的名称和值写入流中

    2,格式化器如何自动反序列化类型应用了SerializableAttribute特性的对象
    ①格式化器流中读取程序集标识和完整类名称。如果程序集当没有加载到AppDomain中,就加载它。如果程序集不能加载,就抛出一个SerializetionException异常,对象不能反序列化。如果程序集已加载,格式化器将程序集标识信息和类型全名传给FormatterServices的静态方法GetTypeFromAssembly

     public static Type GetTypeFromAssembly(Assembly assem, string name);

    这个方法返回一个System.Type对象,它代表要反序列化的那个对象的类型

    ②格式化器调用FormatterServices的静态方法GetUninitializedObject

    public static object GetUninitializedObject(System.Type type)

    这个方法为一个新对象分配内存,但不为对象调用构造器。然而,对象的所有字节都被初始化成null或0

    ③格式化器现在构造并初始化一个MemberInfo数组,具体做法和前面一样,都是调用FormatterServices的GetSerializableMembers方法。这个方法返回序列化好、现在需要反序列化的一组字段

    ④格式化器根据流中包含的数据创建并初始化一个Object数组

    ⑤将新分配对象、MemberInfo数组以及并行Object数组(其中包含字段值)的引用传给FormatterServices的静态方法PopulateObjectMembers

    public static object PopulateObjectMembers(object obj, System.Reflection.MemberInfo[] members, object[] data)

    这个方法遍历数组,将每个字段初始化成对应的值。

    五、控制序列化/反序列化的数据

    1,ISerializable

    格式化器内部使用的是反射,反射的速度比较慢。为了对序列化和反序列化完全控制,需要实现System.Runtime.Serialization.ISerializable接口。派生此接口的类型最好是派生类。

        public interface ISerializable
        {
            void GetObjectData(SerializationInfo info,StreamingContext context){}
        }

    建议向GetObjectData方法和特殊构造器应用以下特性

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]

    2,SerializationInfo(包含了要为对象序列化的值得集合)

    构造此对象需要传递两个参数Type和System.Runtime.Serialization.IFormatterConverter

    SerializationInfo对象的AddValue方法添加要序列化的信息 

        class Program
        {
            static void Main(string[] args)
            {
                CustomerSerializeable c = new CustomerSerializeable() {Name = "张三", Age =12};
    
                MemoryStream ms = new MemoryStream();
                BinaryFormatter b = new BinaryFormatter();
                b.Serialize(ms, c);
                ms.Position = 0;
                var aa=(CustomerSerializeable)b.Deserialize(ms);
                Console.WriteLine(aa.Name);
                Console.WriteLine(aa.Age);//不会被序列化
    
                Console.ReadKey();
            }
        }
    
        //设置此类型最好是密封的
        [Serializable]
        public sealed class CustomerSerializeable : ISerializable,IDeserializationCallback
        {
            public string Name { get; set; }
            public int Age { get; set; }
    
            //只用于反序列化
            private SerializationInfo m_siInfo;
    
            public CustomerSerializeable(){}
    
            //用于控制反序列化的特殊构造器(不加此构造函数反序列化将抛异常)
            [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
            private CustomerSerializeable(SerializationInfo info, StreamingContext sc)
            {
                m_siInfo = info;
            }
    
            //用于控制序列化的方法
            [SecurityCritical]
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Name", Name, typeof (string));
            }
    
            //所有key/value对象都反序列化好之后调用的方法
            public void OnDeserialization(object sender)
            {
                if(m_siInfo==null)return;//从不设置,直接返回
                Name = m_siInfo.GetString("Name");
            }
        }

    如果一个字段的类型实现了ISerializable接口,就不要在字段上调用GetObjectData。相反,调用AddValue来添加字段

     3,IFormatterConverter

    FormatterConverter类型调用System.Convert类的各种静态方法在不同的核心类型之间对值进行转换

    4,要实现 ISerializable但基类型没有实现怎么办?

        [Serializable]
        public class Base
        {
            protected string m_name = "张三";
        }
    
        [Serializable]
        public class Derived : Base,ISerializable
        {
            private DateTime m_date=DateTime.Now;
    
            public Derived(){}
    
            //用于控制反序列化的特殊构造器(不加此构造函数反序列化将抛异常)
            [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
            private Derived(SerializationInfo info, StreamingContext context)
            {
                m_date = info.GetDateTime("Date");
    
                Type baseType = GetType().BaseType;
                MemberInfo[] mis = FormatterServices.GetSerializableMembers(baseType, context);//获取类型所有可序列化的成员
                //从info对象反序列化基类的字段
                foreach (var memberInfo in mis)
                {
                    FieldInfo f = (FieldInfo) memberInfo;
                    f.SetValue(this, info.GetValue(baseType.FullName + "+" + f.Name, f.FieldType));
                }
            }
            [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.AddValue("Date", m_date);
                Type baseType = GetType().BaseType;
                MemberInfo[] mis = FormatterServices.GetSerializableMembers(baseType, context);//获取类型所有可序列化的成员
                //将基类字段序列化到info对象中
                foreach (var memberInfo in mis)
                {
                    FieldInfo f = (FieldInfo)memberInfo;
                    info.AddValue(baseType.FullName + "+" + f.Name,f.GetValue(this));
                }
            }
    
            public override string ToString()
            {
                return string.Format("Name={0},Date={1}", m_name, m_date);
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
    
                Derived c = new Derived();
    
                MemoryStream ms = new MemoryStream();
                BinaryFormatter b = new BinaryFormatter();
                b.Serialize(ms, c);
                ms.Position = 0;
                var aa=(Derived)b.Deserialize(ms);
                Console.WriteLine(aa);
    
                Console.ReadKey();
            }
        }

    六、序列化和反序列化单实例

        class Program
        {
            static void Main(string[] args)
            {
                Singleton[] ss = new Singleton[] {Singleton.GetSingleton(), Singleton.GetSingleton()};
                Console.WriteLine(ss[0]==ss[1]);//True
    
                using (MemoryStream ms = new MemoryStream())
                {
                    BinaryFormatter formatter = new BinaryFormatter();
                    formatter.Serialize(ms, ss);
                    ms.Position = 0;
                    var ss2 = (Singleton[]) formatter.Deserialize(ms);
                    Console.WriteLine(ss2[0] == ss2[1]);//True
                    Console.WriteLine(ss[0] == ss2[0]);//True
                }
                Console.ReadKey();
            }
        }
    
        [Serializable]
        public sealed class Singleton : ISerializable
        {
    
            private static Singleton s_theOneObject = new Singleton();
            public string Name = "张三";
            public  DateTime Date=DateTime.Now;
    
            private Singleton(){}
    
            public static Singleton GetSingleton(){return s_theOneObject;}
    
    
            [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
            public void GetObjectData(SerializationInfo info, StreamingContext context)
            {
                info.SetType(typeof(SingletonSerializationHepler));
            }
    
            [Serializable]
            private sealed class SingletonSerializationHepler : IObjectReference
            {
                //这个方法在对象(他没有字段)反序列化之后调用
                public object GetRealObject(StreamingContext context)
                {
                    return GetSingleton();
                }
            }
        }

     七、序列化代理

    class Program
        {
            static void Main(string[] args)
            {
    
                using (var ms = new MemoryStream())
                {
                    IFormatter formatter = new BinaryFormatter();
    
                    //构造一个SurrogateSelector(代理选择器)对象
                    SurrogateSelector ss = new SurrogateSelector();
                    //告诉代理选择器为Datetime对象使用我们的代理
                    ss.AddSurrogate(typeof (DateTime), formatter.Context, new LocalTimeSerializationSurrogate());
                    //注意:AddSurrogate可多次调用来登记多个代理
    
                    //告诉格式化选择器使用代理对象
                    formatter.SurrogateSelector = ss;
    
                    //创建一个DateTime来代表机器上的本地时间,并序列化它
                    DateTime localDateTime = DateTime.Now;
                    formatter.Serialize(ms, localDateTime);
    
                    //反序列化
                    ms.Position = 0;
                    var dt = (DateTime) formatter.Deserialize(ms);
    
                    Console.WriteLine(dt);
                }
    
                Console.ReadKey();
            }
        }
    
    
        public sealed class LocalTimeSerializationSurrogate : ISerializationSurrogate
        {
            public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
            {
                //将DateTime从本地时间转换成UTC
                info.AddValue("Date", ((DateTime) obj).ToShortDateString());
            }
    
            public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
            {
                //将DateTime从UTC转换成本地时间
                return Convert.ToDateTime(info.GetString("Date"));
            }
        }
  • 相关阅读:
    23. call和apply和bind的区别
    22.call方法的深入
    21.函数的三种角色
    20.原型深入
    2.9 原型链综合复习参考
    2.8深入扩展原型链模式常用的六种继承方式
    2.7原型链模式扩展-批量设置公有属性
    php数组函数有哪些操作?php数组函数的应用
    PHP常见的一些问题总结(收藏)
    yii框架 隐藏index.php 以及美化URL(pathinfo模式访问)
  • 原文地址:https://www.cnblogs.com/zd1994/p/7231084.html
Copyright © 2011-2022 走看看