.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. ...
随机推荐
- string类总结
头文件: <string> 初始化: string str(s1); string str("value"); , 'c'); 读写 //输入未知数目的string对象 ...
- 解析文本文件 "r" 与 "rb" 模式的区别(Python)
r,rb 那么在读文件时,有无b标识的的主要区别在哪里呢? 1.文件使用方式标识 'r':默认值,表示从文件读取数据.'b':表示要读写二进制数据 2.读文件 进行读文件操作时,直到读到文档结束符(E ...
- 002 python语法入门
一:基本数据类型知识点 1.基本数据类型 Number 数字 String 字符串 Bool 布尔 List 列表 Tuple 元组 Set 集合 Dictionary字典 2.分类 )标准的pyth ...
- PHP老师没教过你的那些知识点
另类的写法有惊喜 我们在阅读某些源代码的时候会发现有一种另类的写法,比如 //异常写法 if(false == $result) //正常写法 if($result == false) 其实这是一 ...
- 使用IDEA和Maven创建Javaweb项目
1.File -- New -- Project
- SpringBoot常用配置
前言:springboot集成了主流的第三方框架,但是需要使用springboot那一套配置方式.但是我这里只列举了非常非常常用的,可以看已发的几篇博客,慢慢会补充.当然官方文档里也有相应的配置,可惜 ...
- Effective C++ —— 模板与泛型编程(七)
C++ templates的最初发展动机很直接:让我们得以建立“类型安全”的容器如vector,list和map.然而当愈多人用上templates,他们发现templates有能力完成愈多可能的变化 ...
- [ 原创 ]学习笔记-三种向ListView中填充简单文本的方法
Android 中ListView是很重要的一块内容 掌握ListView的基本用法 对学习安卓起着举足轻重的作用 今天就介绍一下三种向ListView 填充简单文本的方法 填充其他数据类型的用法之后 ...
- [BZOJ5303][HAOI2018]反色游戏(Tarjan)
暴力做法是列异或方程组后高斯消元,答案为2^自由元个数,可以得60分.但这个算法已经到此为止了. 从图论的角度考虑这个问题,当原图是一棵树时,可以从叶子开始唯一确定每条边的选择情况,所以答案为1. 于 ...
- 转 TCP/IP的三次握手与四次挥手详解
TCP((Transmission Control Protocol)传输控制协议,是一个面向连接的协议.在运用此协议进行数据传输前都会进行连接的建立工作(三次握手):当数据传输完毕,连接的双方都会通 ...