clr via c# 运行时序列化
1,快速了解序列化----windows IO 系统,FileStream,BinaryFormatter,SoapFormatter--不支持泛型.
public class SerializeRef { public static void CallQuick() { dynamic objectgrap = new List<string>() { "jeff", "kristin", "aldan", "grant" }.ToArray<string>(); using (var st = new FileStream("mxb.xml", FileMode.Create)) { SoapFormatter formatter = new SoapFormatter(); formatter.Serialize(st, objectgrap); st.Position = 0; objectgrap = null; var objectgrap1 = ((string[])formatter.Deserialize(st)).ToList<string>(); objectgrap1.ForEach(x => Display(0, x)); } } private static void Display(int indent, string Format, params object[] obj) { Console.Write(new string(' ', indent * 2)); Console.WriteLine(Format, obj); } }
利用BinaryFormatter.Serialize配合流进行对象图的写入.
利用BinaryFormatter.DeSerialize进行对象图的还原
流对象st.Postion如果没设置会报错.
2, 利用序列化创建对象的深拷贝.
private static object DeepClone(object ori) { using(MemoryStream ms=new MemoryStream()) { BinaryFormatter bf = new BinaryFormatter(); bf.Context = new StreamingContext(StreamingContextStates.Clone); bf.Serialize(ms, ori); ms.Position = 0; return bf.Deserialize(ms); } }
//注意Context中的一个用法,表明这是一个流的一个克隆过程,告诉其他线程该如何处理原对象.
3,利用序列化保存和恢复程序的状态.
public static void CallSerilalMultiple() { List<Customer> s_customers = new List<Customer>() { new Customer("abc"),}; List<order> s_orders = new List<order>() { new order("abc"), }; using(var fs=new FileStream("mxb.dat", FileMode.OpenOrCreate)) { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, s_customers); bf.Serialize(fs,s_orders); s_customers = null; s_orders = null; GC.Collect(); } using (var fs = new FileStream("mxb.dat", FileMode.OpenOrCreate)) { BinaryFormatter bf = new BinaryFormatter(); s_customers = (List<Customer>)(bf.Deserialize(fs)); s_orders = (List<order>)(bf.Deserialize(fs)); } s_customers.ForEach(x => Display(0, x.name)); s_orders.ForEach(x => Display(0, x.name)); }
//result
Customerabc ;成功的反序列化原来的对象.
orderabc;成功反序列化原来的对象.
4,让对象可序列化 定制特性SerializableAttribute
只能用于 class,struct,枚举类型和委托类型(后两者总是可序列化).
Serializable特性不被派生类继承,
5,控制序列化
序列化应用于类型时,所有的实列字段都会被序列化.(类的对象),使用NonSerialized,这样,某个字段就不会被序列化.当反序列化的时候,该字段自动清0.
类可设定四个方法进行控制:
- private void OnSerializing(StreamContext context)---序列化之前[OnSerializing]
- private void OnSerialized(streamContext context)---序列化完成后[OnSerialized]
- private void OnDeSerialized(streamContext context)---反序列化之前[OnDeSerializing]
- private void OnDeSerialized(stream Context context)---反序列化之后[OnDeSerialized]
序列化一组对象前,先调用标记OnSerializing特性的所有方法.接着序列化所有字段,接着调用标记OnSerialized特性的所有方法.
反序列化前,先调用OnDeserializing特性的所有方法,然后反序列化所有的对象字段,然后调用标记了OnDeserialized方法.
运行OnDeserialized方法前,会进行由内而外的反序执行(先执行对象指向对象的方法,最后才是对象的方法).
6,格式化器序列化类型实列的过程
1,使用FormatterServices.GetSerializableMembers,获取(public,private)的可序列化字段
一个MemberInfo[] 数组
2,调用FormatterServices.GetObjectData(obj,memberinfos)来获取各个字段的data,一个object[]数组,和上面对应.
public static void CallHowSerialize() { Customer ct = new Customer("nameadef"); //1,通过该函数获得类的public和private的字段. MemberInfo[] mis= FormatterServices.GetSerializableMembers(typeof(Customer));//可以获取隐藏的属性字段.可获取私有字段. Array.ForEach<MemberInfo>(mis, x => Display(0,"{0}", x.Name)); //获取字段的值... Display(0, ""); object[] objs = FormatterServices.GetObjectData(ct, mis); Array.ForEach<object>(objs, x => Display(0, "{0}", x.ToString())); }
3,将程序集标识和类型完整名称写入流中
4,遍历两个数组的元素,将名称和值写入流中
7,反序列化实列的流程
1,格式化器从流中读取程序集标识和完整的类名称,如果程序集未加载,就试图加载程序集.如果已经加载,就调用函数
FormatterServices.GetTypeFromAssembly(assembly,string name)获得需要反序列化的对象的类型.
2,格式化器调用FormatterServices的静态方法
public static object GetUninitializedObject(Type type)为对象分配内存,但是不为对象调用构造器.然而,所有的字节都
被初始化为null,或0;
3,格式化器现在构造,并初始化一个MemberInfo数组,获得MemberInfo[]数组,表示需要反序列化的字段
4,格式化器根据流中包含的字段值反序列化为一个object[]数组.
5,将新分配的对象,MemberInfo数组以及Object数组的引用传递给FormatterServices的静态方法
public static object PopulateObjectMembers( object obj, MemberInfo[] mis, object[] datas);
举列:----
public static void CallHowSerialize() { Customer ct = new Customer("nameadef"); //1,通过该函数获得类的public和private的字段. MemberInfo[] mis= FormatterServices.GetSerializableMembers(typeof(Customer));//可以获取隐藏的属性字段.可获取私有字段. //无法获取隐藏的属性字段. Display(0, "Filds to be Serialized:"); Array.ForEach<MemberInfo>(mis, x => Display(1,"{0}", x.Name)); //获取字段的值... Display(0, "Filds Data to be Serialized:"); object[] objs = FormatterServices.GetObjectData(ct, mis); Array.ForEach<object>(objs, x => Display(1, "{0}", x.ToString())); //反序列化对象 Customer dct = null; //获得需要反序列化对象的类型: Type t = FormatterServices.GetTypeFromAssembly(Assembly.GetEntryAssembly(), typeof(Customer).FullName); //创建一个初始化但未调用构造器的类对象 dct = (Customer)FormatterServices.GetUninitializedObject(t); Display(0, "\n Before Deserialized FieldsData"); Display(1,"name is {0}",dct.name??"None"); dct =(Customer) FormatterServices.PopulateObjectMembers(dct, mis, objs); Display(0, "\n After Deserialized FieldsData"); Display(0, "name is {0}", dct.name); }
Filds to be Serialized:
name
t
<Props>k__BackingField
Filds Data to be Serialized:
Customernameadef
0
0
Before Deserialized FieldsData
name is None
After Deserialized FieldsData
name is Customernameadef
1,反序列化过程不调用构造器
2,通过上面函数可以设定私有字段.
8,控制序列化和反序列化的数据----ISerializable接口:
void GetObjectData(SerializationInfo info,StreamingContext context)----一旦某个类实现了它,其派生类也必须实现.(所以,实现了该接口的类最好是密封的sealed).
如果有该接口,那么在序列化的时候,开始使用info.add添加(key,可序列化对象).然后序列化SerializationInfo对象,并序列化其中引用的所有序列化对象.
在反序列化的时候,就反序列化SerializationInfo对象,然后调用反序列化构造器,将这个参数和StreamingContext对象传递给反序列化构造器.
当使用GetValue对象获得的对象和你视图获取的对象不符合,则将调用对象的IFormatterConverter对象,将 GetValue取得的对象进行转换
s2 = (SerializeTwo)info.GetValue("s2", typeof(object));//后一个是获得对象要转换的类型.
1,首先创建一个类 SerializeOne
首先该类有3个字段.
该类有两个构造器
该类有一个反序列化构造器
该类的序列化函数将字段加入info
该类的反序列化构造器将信息读出并显示
internal class SerializedOne:ISerializable,IDeserializationCallback { private string m_name; private int m_int; private List<string> list=new List<string>(); public SerializedOne():this("initial",default(int),null) { } public SerializedOne(string name,int int1,params string[] strs) { m_name = name; m_int = int1; if(strs!=null)list.AddRange(strs); } public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("List", list); info.AddValue("m_name", m_name); info.AddValue("m_int", m_int); } public void OnDeserialization(object sender) { Console.WriteLine("DeSerialized!"); } protected SerializedOne(SerializationInfo info,StreamingContext context)//反序列化构造器,进行反序列化一个新的未构造对象后,进行运行; { list =(List<string>) info.GetValue("List",typeof(object)); m_int = (int)info.GetValue("m_int", typeof(int)); m_name = (string)info.GetValue("m_name", typeof(string)); Console.WriteLine("DeSerialized Constructor"); foreach(var enter in info) { Console.WriteLine(enter.Name+" : "+enter.Value); } } public override string ToString() { string str=string.Format("\nm_name={0}:m_int={1}", m_name, m_int); int index = 0; list.ForEach(x => str = str + string.Format("\nItem {0} = {1} ", index++, x)); return str; } public virtual void Display() { Console.WriteLine(this.ToString()); } }
2,创建一个类2继承自类1..(类2同时继承了类1的所有字段)
该类重写了类1的几个方法
internal class SerializeTwo:SerializedOne,ISerializable { public string name; public SerializeTwo(string name):base(name,100,"abc","efg") { this.name = name; } private SerializeTwo(SerializationInfo info, StreamingContext context):base(info,context)//反序列化构造器,进行反序列化一个新的未构造对象后,进行运行; { name = (string)info.GetValue("name", typeof(string)); } public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("name", this.name); } public override string ToString() { return base.ToString() + string.Format("\nSerializedTwo's name ={0}", this.name); } public override void Display() { Console.WriteLine(ToString()); } }
3,进行测试:
public static void CallISerializedTwo() { Console.WriteLine("Show Fileds of SerializeTwo [[[[[:--------------"); MemberInfo[] mis = FormatterServices.GetSerializableMembers(typeof(SerializeTwo));//可以获取隐藏的属性字段.可获取私有字段. Array.ForEach<MemberInfo>(mis, x => Display(1, "{0}", x.Name)); Console.WriteLine("Show Fileds of SerializeTwo ]]]]:--------------"); SerializeTwo so = new SerializeTwo("mxb"); so.Display(); using (var fs = new FileStream("mxb.dat", FileMode.OpenOrCreate)) { SerializedGJ.Serialize(fs, new BinaryFormatter(), so); } so = null; using (var fs = new FileStream("mxb.dat", FileMode.OpenOrCreate)) { so = (SerializeTwo)SerializedGJ.DeSerialize(fs, new BinaryFormatter()); so.Display(); } }
Show Fileds of SerializeTwo [[[[[:--------------
name
SerializedOne+m_name
SerializedOne+m_int
SerializedOne+list
Show Fileds of SerializeTwo ]]]]:--------------
m_name=mxb:m_int=100
Item 0 = abc
Item 1 = efg
SerializedTwo's name =mxb
DeSerialized Constructor
List : System.Collections.Generic.List`1[System.String]
m_name : mxb
m_int : 100
name : mxb
DeSerialized!
m_name=mxb:m_int=100
Item 0 = abc
Item 1 = efg
SerializedTwo's name =mxb
结果: 1,表明派生类继承了基类的字段
2,当未序列化的时候,各个字段的值
3,将so引用清0,然后进行反序列化
4,显示了info类的Key,value内容,
5,显示类2的所有字段的值.
9,利用如何序列化一个没有被标记为序列化的类
[Serializable] internal sealed class NoSerializableToSerialized:ISerializable //我写的一个可以包装未设置序列化类的类. { private object m_obj; private const BindingFlags c_bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField; public object Obj { get { return m_obj; } } public NoSerializableToSerialized(object obj) { m_obj = obj; } private NoSerializableToSerialized(SerializationInfo info, StreamingContext context) { Type t = (Type)info.GetValue("TypeOfObject", typeof(Type));//反序列化m_obj的对象类型. m_obj = Activator.CreateInstance(t);//创建类型实列. FieldInfo[] mis = null; if (m_obj.GetType().IsDefined(typeof(SerializableAttribute)))//如果是序列化类调用.... { mis = (FieldInfo[])FormatterServices.GetSerializableMembers(m_obj.GetType()); } else//非序列化类调用.... { mis = m_obj.GetType().GetTypeInfo().GetFields(c_bf); } for (int i = 0; i < mis.Length; i++) { var mi = mis[i]; if (!mi.FieldType.GetTypeInfo().IsDefined(typeof(SerializableAttribute)))//非序列化类,迭代解析. { var objss = (NoSerializableToSerialized)info.GetValue(mi.Name + i, typeof(NoSerializableToSerialized)); mi.SetValue(m_obj, objss.Obj); } else//否则直接获得对象. { mi.SetValue(m_obj, info.GetValue(mi.Name + i, mi.FieldType)); } } } //自定义序列化函数 public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("TypeOfObject", m_obj.GetType(), typeof(Type));//将对象类型序列化 FieldInfo[] mis = null; object[] objs = null; if (m_obj.GetType().IsDefined(typeof(SerializableAttribute)))//如果是序列化类型,调用GetSerializableMembers { mis =(FieldInfo[])FormatterServices.GetSerializableMembers(m_obj.GetType()); objs = FormatterServices.GetObjectData(m_obj, mis); Console.WriteLine("\n defined serial"); } else//如果是非序列化,调用GetFields. { mis = m_obj.GetType().GetTypeInfo().GetFields(c_bf); objs = FormatterServices.GetObjectData(m_obj, mis); } for (int i = 0; i < mis.Length; i++) { var mi = mis[i]; var obj = objs[i]; //如果Field是非序列化对象,则进行包装,再序列化.否则直接加入Info if (!mi.FieldType.GetTypeInfo().IsDefined(typeof(SerializableAttribute))) { info.AddValue(mi.Name + i, new NoSerializableToSerialized(obj)); } else { info.AddValue(mi.Name + i, obj); } } } }
10,单实列对象序列化
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; } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(SingletionSerializationHelper)); } private Singleton(SerializationInfo info, StreamingContext context)//该函数未运行. { Console.WriteLine("Deserialization Singletion"); } [Serializable] private sealed class SingletionSerializationHelper : IObjectReference { public object GetRealObject(StreamingContext context) { return Singleton.GetSingleton(); } } }
1,使用SetType表示在序列化的时候将序列化一个SingletionSerializationHelper对象,----将该对象的程序集和类型信息写入序列化中
2,在反序列化的时候,格式化器知道这是一个由类型转换来的对象.所以它首先反序列化成一个SingletionSerializationHelper对象.
注意:该对象必须实现接口IObjectReference,然后调用该对象的GetRealObject对象---让它返回一个所需要的对象.(这个方法主要用于对单实列类进行保存.
11,序列化代理:(利用代理可以重写和覆盖,将类型反馈为一个不同的版本)
序列化代理必须事件ISerializationSurrogate接口
该接口实现了2个函数:
GetObjectData(Object, SerializationInfo, StreamingContext) //用序列化对象所需的数据填充所提供的 SerializationInfo。 SetObjectData(Object, SerializationInfo, StreamingContext, ISurrogateSelector) 反序列化时,利用其将SerializationInfo中
- 当某个类型 和 序列化代理关联的时候,那么,序列化这个对象,就调用了GetObjectData方法,(ISerializable)中的方法.并且
第一个参数为,该类型的实列,第二个参数为格式化器提供的Info.
当反序列化的时候,首先使用FormatterServices.GetUninitializedObject()建立一个关联类型的未初始化的实列.该实列的所有的Field是NUll,或者0;
然后这个参数作为第一个参数传给SetObjectData,这个参数可用,可不用.
- 首先创建一个需要代理的类(测试用,非实现Serializable)
public class TestForSurrogate//一个测试类,当序列化的时候,不管序列化时间,反序列化的时候,更新其时间 { public DateTime dt = DateTime.Now; public String Name; public TestForSurrogate(string name) { Name = name; } }
- 然后创建一个代理类,一个实现了ISerializationSurrogate接口的类
public sealed class MySurrogateForSurrogateTest : ISerializationSurrogate { public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { TestForSurrogate Myboj = (TestForSurrogate)obj; Myboj.dt = DateTime.Now; Console.WriteLine(Myboj.Name+Myboj.dt.ToString()); info.AddValue("Name", Myboj.Name); info.AddValue("time", Myboj.dt); } public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { TestForSurrogate Myboj = (TestForSurrogate)obj; Myboj.dt = (DateTime)info.GetValue("time",typeof(object)); Myboj.Name = (string)info.GetValue("Name", typeof(object)); Console.WriteLine(Myboj.Name + Myboj.dt.ToString()); return Myboj; } }
然后创建一个surrogateSelector对象,并且进行绑定
SurrogateSelector selector = new SurrogateSelector(); selector.AddSurrogate(typeof(TestForSurrogate), formatter.Context, new MySurrogateForSurrogateTest());
然后将其赋值给,Formmater的SurrogateSelecotor属性
formatter.SurrogateSelector = selector;
- 然后就能工作了,当序列化 一个TestForSurrogate对象,就会调用MySurrogateForSurrogateTest.GetObjectData
- 当反序列化时候,就会调用MySurrogateForSurrogateTest.SetObjectData
注意,Selector实现了接口ISurrogateSelector接口:
ChainSelector 将Selector链接起来
GetNextSelector 获得下一个Selector
GetSurrgate,在Selector链中查找.
相当于KeyValue<type,Surrogate)----组成了一个 Selector,
Selector.Chain(Selector1)将Selector1加入到Selector链中.
12,反序列化的时候重写程序集/类型-----------SerializationBinder对象的使用.
1,自己实现一个SerializationBinder的派生类.
public class MySerializationBinder : SerializationBinder { public override Type BindToType(string assemblyName, string typeName) { Console.WriteLine("BInderToType..."); Console.WriteLine("\n" + assemblyName); Console.WriteLine("\n" + typeName); //return Type.GetType(typeName +","+ assemblyName);//加,号,因为否则生成的typename是不完整的. return typeof(TestForSurrogate1); } }
2,其赋值给Formmater.Binder
formatter.Binder = new MySerializationBinder();
3,实现绑定类型类,将---类TestForSurrogate返回给类TestForSurrogate1
public sealed class TestForSurrogate1:ISerializable//一个测试类,当序列化的时候,不管序列化时间,反序列化的时候,更新其时间 { public DateTime dt = DateTime.Now; public String Name; public TestForSurrogate1(string name) { Name = name; } private TestForSurrogate1(SerializationInfo info, StreamingContext context) { Console.WriteLine("test1 Deserialized.."); try { info.GetValue("Test1", typeof(TestForSurrogate1)); } catch(Exception) { Console.WriteLine("it's From Surrogate v1.0"); this.Name = (string)info.GetValue("Name", typeof(object)); this.dt = DateTime.Now; } } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Test1", this); } }
4,测试如下代码
public static void Call1() { using (var st = new FileStream("mxb.xml", FileMode.Create)) { SoapFormatter formatter = new SoapFormatter(); SurrogateSelector selector = new SurrogateSelector(); selector.AddSurrogate(typeof(TestForSurrogate), formatter.Context, new MySurrogateForSurrogateTest()); formatter.SurrogateSelector = selector; formatter.Binder = new MySerializationBinder(); formatter.Serialize(st, new TestForSurrogate("mxb")); //必须,表明要格式化的位置? st.Position = 0; var objectgrap1 = formatter.Deserialize(st); Console.WriteLine(objectgrap1.GetType().ToString()); Console.WriteLine(((TestForSurrogate1)objectgrap1).Name); } } }
其结果如下:
mxb2020/2/19 21:32:55 BInderToType... ClrFromCSharp_2_2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=11fccde6e91ad1e9 ClrFromCSharp_2_2.LearnSerialize.TestForSurrogate test1 Deserialized.. it's From Surrogate v1.0 ClrFromCSharp_2_2.LearnSerialize.TestForSurrogate1 mxb
xml文件内容如下
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <a1:Singleton_x002B_SingletionSerializationHelper id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/ClrFromCSharp_2_2.LearnSerialize/ClrFromCSharp_2_2%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3D11fccde6e91ad1e9"> <Name id="ref-3">Jeff</Name> </a1:Singleton_x002B_SingletionSerializationHelper> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
表明对象序列化的绑定器和绑定的类别. ------------就是创建了一个单实列的SingletionSerializationHelper.
当反序列化的时候,格式化器通过selector知道这个Selector绑定的类别
然后创建该类别非初始化实列
然后传递给SetObjectData的第一个参数.执行这个函数.
--------------
当由Binder之后,对象在反序列化的时候,首先
- 执行Binder的BinderToType函数,然后返回了一个Type.
- 然后,如果该类型有ISerialize接口,调用反序列化构造器.
- 否则,调用默认反序列化过程,见上面.
上面的列子很有意思
由于Selector和Surrogate的缘故,首先当序列化一个TestForSurrogate对象的时候,会先调用代理的序列化函数.
但是,由于binder的作用,再返回的时候,因为,创建了一个TestForSurrogate1对象,会调用TestForSurrogate1,的反序列化构造器.