运行时序列化

 一、序列化与反序列化

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

    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"));
        }
    }
原文地址:https://www.cnblogs.com/zd1994/p/7231084.html