.NET:CLR via C#:Runtime Serialization
Making a Type Serializable
The SerializableAttribute custom attribute may be applied to reference types (class), value types (struct), enumerated types (enum), and delegate types (delegate) only. (Note that enumerated and delegate types are always serializable, so there is no need to explicitly apply the SerializableAttribute attribute to these types.) In addition, the SerializableAttribute attribute is not inherited by derived types.
Do not use C#’s automatically implemented property feature to define properties inside types marked with the [Serializable] attribute, because the compiler generates the names of the fields and the generated names can be different each time that you recompile your code, preventing instances of your type from being deserializable.
Use of the System.Runtime.Serialization.OnDeserialized custom attribute is the preferred way of invoking a method when an object is deserialized, as opposed to having a type implement the System.Runtime.Serialization. IDeserializationCallback interface’s OnDeserialization method.
When you are serializing a set of objects, the formatter first calls all of the objects’ methods that are marked with the OnSerializing attribute. Next, it serializes all of the objects’ fields, and finally it calls all of the objects’ methods marked with the OnSerialized attribute. Similarly, when you deserialize a set of objects, the formatter calls all of the objects’ methods that are marked with the OnDeserializing attribute, then it deserializes all of the object’s fields, and then it calls all of the objects’ methods marked with the OnDeserialized attribute.
during deserialization, when a formatter sees a type offering a method marked with the OnDeserialized attribute, the formatter adds this object’s reference to an internal list. After all the objects have been deserialized, the formatter traverses this list in reverse order and calls each object’s OnDeserialized method. When this method is called, all the serializable fields will be set correctly, and they may be accessed to perform any additional work that would be necessary to fully deserialize the object. Invoking these methods in reverse order is important because it allows inner objects to finish their deserialization before the outer objects that contain them finish their deserialization.
If you serialize an instance of a type, add a new field to the type, and then try to deserialize the object that did not contain the new field, the formatter throws a SerializationException with a message indicating that the data in the stream being deserialized has the wrong number of members. This is very problematic in versioning scenarios where it is common to add new fields to a type in a newer version. Fortunately, you can use the System.Runtime.Serialization.OptionalFieldAttribute attribute to help you.
You apply the OptionalFieldAttribute attribute to each new field you add to a type. Now, when the formatters see this attribute applied to a field, the formatters will not throw the SerializationException exception if the data in the stream does not contain the field.
测试代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.IO;
- namespace SerializationStudy
- {
- class AttributeSerializable
- {
- public static void Test()
- {
- using (MemoryStream stream = new MemoryStream())
- {
- var formater = new BinaryFormatter();
- formater.Serialize(stream, new TestClassA() { A1 = new TestClassA1 { X = } });
- stream.Position = ;
- var test = formater.Deserialize(stream) as TestClassA;
- Console.WriteLine(test.A1.X);
- }
- }
- }
- [Serializable]
- public sealed class TestClassA
- {
- public TestClassA1 A1 = new TestClassA1();
- [OnSerializing]
- private void OnSerializing(StreamingContext context)
- {
- Console.WriteLine("TestClassA OnSerializing");
- }
- [OnSerialized]
- private void OnSerialized(StreamingContext context)
- {
- Console.WriteLine("TestClassA OnSerialized");
- }
- [OnDeserializing]
- private void OnDeserializing(StreamingContext context)
- {
- Console.WriteLine("TestClassA OnDeserializing");
- }
- [OnDeserialized]
- private void OnDeserialized(StreamingContext context)
- {
- Console.WriteLine("TestClassA OnDeserialized");
- }
- }
- [Serializable]
- public sealed class TestClassA1
- {
- public int X;
- [OnSerializing]
- private void OnSerializing(StreamingContext context)
- {
- Console.WriteLine("TestClassA1 OnSerializing");
- }
- [OnSerialized]
- private void OnSerialized(StreamingContext context)
- {
- Console.WriteLine("TestClassA1 OnSerialized");
- }
- [OnDeserializing]
- private void OnDeserializing(StreamingContext context)
- {
- Console.WriteLine("TestClassA1 OnDeserializing");
- }
- [OnDeserialized]
- private void OnDeserialized(StreamingContext context)
- {
- Console.WriteLine("TestClassA1 OnDeserialized");
- }
- }
- }
运行效果
Controlling the Serialized/Deserialized Data
To get complete control over what data is serialized/deserialized or to eliminate the use of reflection,your type can implement the System.Runtime.Serialization.ISerializable interface, which is defined as follows.
- public interface ISerializable
- {
- void GetObjectData(SerializationInfo info, StreamingContext context);
- }
When a formatter serializes an object graph, it looks at each object. If its type implements the ISerializable interface, then the formatter ignores all custom attributes and instead constructs a new System.Runtime.Serialization.SerializationInfo object. This object contains the actual set of values that should be serialized for the object.
Although you can set a SerializationInfo’s FullTypeName and AssemblyName properties, this is discouraged. If you want to change the type that is being serialized, it is recommended that you call SerializationInfo’s SetType method, passing a reference to the desired Type object. Calling SetType ensures that the type’s full name and defining assembly are set correctly.
After the SerializationInfo object is constructed and initialized, the formatter calls the type’s GetObjectData method, passing it the reference to the SerializationInfo object. The GetObjectData method is responsible for determining what information is necessary to serialize the object and adding this information to the SerializationInfo object. GetObjectData indicates what information to serialize by calling one of the many overloaded AddValue methods provided by the SerializationInfo type. AddValue is called once for each piece of data that you want to add.
You should always call one of the overloaded AddValue methods to add serialization information for your type. If a field’s type implements the ISerializable interface, don’t call the GetObjectData on the field. Instead, call AddValue to add the field; the formatter will see that the field’s type implements ISerializable and the formatter will call GetObjectData for you. If you were to call GetObjectData on the field object, the formatter wouldn’t know to create a new object when deserializing the stream.
The formatter takes all of the values added to the SerializationInfo object and serializes each of them out to the stream.
let’s turn our attention to deserialization. As the formatter extracts an object from the stream, it allocates memory for the new object (by calling the System.Runtime.Serialization.FormatterServices type’s static GetUninitializedObject method). Initially, all of this object’s fields are set to 0 or null. Then, the formatter checks if the type implements the ISerializable interface. If this interface exists, the formatter attempts to call a special constructor whose parameters are identical to that of the GetObjectData method.
This constructor receives a reference to a SerializationInfo object containing all of the values added to it when the object was serialized. The special constructor can call any of the GetBoolean, GetChar, GetByte, GetSByte, GetInt16, GetUInt16, GetInt32, GetUInt32, GetInt64, GetUInt64, GetSingle, GetDouble, GetDecimal, GetDateTime, GetString, and GetValue methods, passing in a string corresponding to the name used to serialize a value. The value returned from each of these methods is then used to initialize the fields of the new object.
when a SerializationInfo object is constructed, it is passed an object whose type implements the IFormatterConverter interface. Because the formatter is responsible for constructing the SerializationInfo object, it chooses whatever IFormatterConverter type it wants. Microsoft’s BinaryFormatter and SoapFormatter types always construct an instance of the System.Runtime.Serialization.FormatterConverter type. Microsoft’s formatters don’t offer any way for you to select a different IFormatterConverter type.
The FormatterConverter type calls the System.Convert class’s static methods to convert values between the core types, such as converting an Int32 to an Int64. However, to convert a value between other arbitrary types, the FormatterConverter calls Convert’s ChangeType method to cast the serialized (or original) type to an IConvertible interface and then calls the appropriate interface method. Therefore, to allow objects of a serializable type to be deserialized as a different type, you may want to consider having your type implement the IConvertible interface. Note that the FormatterConverter object is used only when deserializing objects and when you’re calling a Get method whose type doesn’t match the type of the value in the stream.
The code in the special constructor typically extracts its fields from the SerializationInfo object that is passed to it. As the fields are extracted, you are not guaranteed that the objects are fully deserialized, so the code in the special constructor should not attempt to manipulate the objects that it extracts. If your type must access members (such as call methods) on an extracted object, then it is recommended that your type also provide a method that has the OnDeserialized attribute applied to it or have your type implement the IDeserializationCallback interface’s OnDeserialization method (as shown in the Dictionary example). When this method is called, all objects have had their fields set. However, there is no guarantee to the order in which multiple objects have their OnDeserialized or OnDeserialization method called. So, although the fields may be initialized, you still don’t know if a referenced object is completely deserialized if that referenced object also provides an OnDeserialized method or implements the IDeserializationCallback interface.
测试代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.IO;
- namespace SerializationStudy
- {
- class InterfaceSerializable
- {
- public static void Test()
- {
- using (MemoryStream stream = new MemoryStream())
- {
- var formater = new BinaryFormatter();
- formater.Serialize(stream, new TestClassB() { X = });
- stream.Position = ;
- var test = formater.Deserialize(stream) as TestClassB;
- Console.WriteLine(test.X);
- }
- }
- }
- [Serializable]
- public sealed class TestClassB : ISerializable, IDeserializationCallback
- {
- public int X;
- public TestClassB()
- {
- }
- private TestClassB(SerializationInfo info, StreamingContext context)
- {
- X = info.GetInt32("X");
- }
- public void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- info.AddValue("X", X);
- }
- [OnSerializing]
- private void OnSerializing(StreamingContext context)
- {
- Console.WriteLine("TestClassB OnSerializing");
- }
- [OnSerialized]
- private void OnSerialized(StreamingContext context)
- {
- Console.WriteLine("TestClassB OnSerialized");
- }
- [OnDeserializing]
- private void OnDeserializing(StreamingContext context)
- {
- Console.WriteLine("TestClassB OnDeserializing");
- }
- [OnDeserialized]
- private void OnDeserialized(StreamingContext context)
- {
- Console.WriteLine("TestClassB OnDeserialized");
- }
- public void OnDeserialization(object sender)
- {
- Console.WriteLine("TestClassB OnDeserialization");
- }
- }
- }
运行结果
Serializing a Type As a Different Type and Deserializing an Object As a Different Object
你可以在 GetObjectData 中调用 SerializationInfo.SetType 修改反序列时创建的类型信息,如果反序列时,创建的类型实现了 System.Runtime.Serialization.IObjectReference 接口:
- public interface IObjectReference
- {
- Object GetRealObject(StreamingContext context);
- }
When a type implements this interface, the formatter calls the GetRealObject method. This method returns a reference to the object that you really want a reference to now that deserialization of the object has completed.
测试代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Runtime.Serialization;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.IO;
- namespace SerializationStudy
- {
- class ObjectReference
- {
- public static void Test()
- {
- using (MemoryStream stream = new MemoryStream())
- {
- var formater = new BinaryFormatter();
- formater.Serialize(stream, new TestClassC());
- stream.Position = ;
- var test = formater.Deserialize(stream);
- Console.WriteLine(test);
- }
- }
- }
- [Serializable]
- public class TestClassC : IObjectReference
- {
- public object GetRealObject(StreamingContext context)
- {
- return "变成字符串了";
- }
- }
- }
输出结果
Serialization Surrogates
Up to now, I’ve been discussing how to modify a type’s implementation to control how a type serializes and deserializes instances of itself. However, the formatters also allow code that is not part of the type’s implementation to override how a type serializes and deserializes its objects.
A serialization surrogate type must implement the System.Runtime.Serialization.ISerializationSurrogate interface, which is defined in the FCL as follows.
- public interface ISerializationSurrogate
- {
- void GetObjectData(Object obj, SerializationInfo info, StreamingContext context);
- Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context,
- ISurrogateSelector selector);
- }
还没看懂的一段话
The BinaryFormatter class has a bug that prevents a surrogate from serializing objects with references to each other. To fix this problem, you need to pass a reference to your ISerializationSurrogate object to FormatterServices’s static GetSurrogateForCyclicalReference method. This method returns an ISerializationSurrogate object, which you can then pass to the SurrogateSelector’s AddSurrogate method. However, when you use the GetSurrogateForCyclicalReference method, your surrogate’s SetObjectData method must modify the value inside the object referred to by SetObjectData’s obj parameter and ultimately return null or obj to the calling method. The downloadable code that accompanies this book shows how to modify the UniversalToLocalTimeSerializationSurrogate class and the SerializationSurrogateDemo method to support cyclical references.
Overriding the Assembly and/or Type When Deserializing an Object
The System.Runtime.Serialization.SerializationBinder class makes deserializing an object to a different type very easy.
The SerializationBinder class also makes it possible to change the assembly/type information while serializing an object by overriding its BindToName method, which looks like this.
During serialization, the formatter calls this method, passing you the type it wants to serialize. You can then return (via the two out parameters) the assembly and type that you want to serialize instead. If you return null and null (which is what the default implementation does), then no change is performed.
.NET:CLR via C#:Runtime Serialization的更多相关文章
- .NET:CLR via C#:CLR Hosting And AppDomains
AppDomain Unloading To unload an AppDomain, you call AppDomain’s Unload static method.This call caus ...
- 基础命名空间:序列化_自定义序列化 System.Runtime.Serialization
( (From Msdn) 自定义序列化是控制类型的序列化和反序列化的过程,通过控制序列化,可以确保序列化兼容性.换而言之,在不中断类型核心功能的情况下,可在类型的不同版本之间序列化和反序列化. 重 ...
- 基础命名空间:序列化 System.Runtime.Serialization
对象通常都有状态(state),从一个对象中抽取这种状态,不论是将它存储于某地,还是通过网络传送,这种抽取动作称为“将一个对象序列化”,而反向处理过程,从一个被序列化的状态重建一个对象即为反序列化. ...
- 格而知之6:我所理解的Runtime(1)
基本简介 1.根据官方文档,OC有一个特性:它会尽可能把一些决定从编译时和链接时推迟到运行时才处理,所以这门语言需要的就不只是一个编译器,它还需要一个runtime系统来处理那些已经被编译过的代码. ...
- 对runtime的总结:让你会用Runtime
导语Runtime,简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制,是一套底层的纯C语言的API,我们平时所编写的OC代码,在程序的运行过程中都转成了runtime的代码,平时调方 ...
- JSTL标签提示:"items" does not support runtime expressions
今天在使用JSTL的 c:forEach 标签时,jsp提示:"items" does not support runtime expressions,后来才发现是因为taglib ...
- 找不到方法:“Boolean System.Runtime.Serialization.DataContractAttribute.get_IsReference()”的解决办法
找不到方法:“Boolean System.Runtime.Serialization.DataContractAttribute.get_IsReference()”.的解决办法站点发布后部署到了两 ...
- 重写成员“log4net.Util.ReadOnlyPropertiesDictionary.GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)”时违反了继承安全性规则
在.NET 4.0下使用最新版本的log4Net 1.2.10,会遇到下面这样的错误: 重写成员“log4net.Util.ReadOnlyPropertiesDictionary.GetObject ...
- System.Runtime.Serialization.SerializationException”类型的未经处理的异常在 System.Runtime.Serialization.dll 中发生
异常信息: “System.Runtime.Serialization.SerializationException”类型的未经处理的异常在 System.Runtime.Serialization. ...
随机推荐
- log4j记录日志到指定文件
新建类文件: import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; /** * 记录日志到指定文件 ...
- RCTF2015 pwn试题分析
pwn200 漏洞给的很明显,先是读到了main的局部数组中,然后在子函数中向子函数的局部数组栈里复制. 总体思路是leak system的地址,然后再向一个固定地址写入/bin/sh,最后执行sys ...
- Entity Framework Code First 在Object Join Linq查询时出现全表查询的语句。
最近一个项目,使用微软的Entity Framework的ORM框架的项目,部署到现场后,出现了系统缓慢,多个客户端的内存溢出崩溃的问题. 打开了SQL Server Profiler(SQL Ser ...
- InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移
系列目录 InterSystems Ensemble学习笔记(一) Ensemble介绍及安装InterSystems Ensemble学习笔记(二) Ensemble创建镜像, 实现自动故障转移 一 ...
- Retrofit + RxJava + OkHttp 让网络请求变的简单-基础篇
https://www.jianshu.com/p/5bc866b9cbb9 最近因为手头上的工作做完了,比较闲,想着做一些优化.看到以前用的那一套网络框架添加一个请求比较麻烦,并且比较难用,所以想改 ...
- mongodb导入导出
导出 mongoexport -d 数据库 -c 表名 -o 输出文件名 例:mongoexport -d Mongodb_DataManager -c Kujiale_Users -o Kujial ...
- linux下文件转码
一.工具介绍 enca是一个很好用的文件转码工具,使用命令 sudo apt-get install enca 即可安装 二.基本用法 1.查看文件编码 $ enca filename 2.文件转码 ...
- JAVAEE——宜立方商城12:购物车实现、订单确认页面展示
1. 学习计划 第十二天: 1.购物车实现 2.订单确认页面展示 2. 购物车的实现 2.1. 功能分析 1.购物车是一个独立的表现层工程. 2.添加购物车不要求登录.可以指定购买商品的数量. 3.展 ...
- 50个必备jQuery代码段
0. 如何创建嵌套的过滤器: 1 2 3 4 5 //允许你减少集合中的匹配元素的过滤器, //只剩下那些与给定的选择器匹配的部分.在这种情况下, //查询删除了任何没(:not)有(:has) // ...
- iOS 11开发教程(十二)iOS11应用视图始祖——UIView
iOS 11开发教程(十二)iOS11应用视图始祖——UIView 在Swift中,NSObject是所有类的根类.同样在UIKit框架(UIKit框架为iOS应用程序提供界面对象和控制器)中,也存在 ...