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的更多相关文章

  1. .NET:CLR via C#:CLR Hosting And AppDomains

    AppDomain Unloading To unload an AppDomain, you call AppDomain’s Unload static method.This call caus ...

  2. 基础命名空间:序列化_自定义序列化 System.Runtime.Serialization

    (  (From Msdn) 自定义序列化是控制类型的序列化和反序列化的过程,通过控制序列化,可以确保序列化兼容性.换而言之,在不中断类型核心功能的情况下,可在类型的不同版本之间序列化和反序列化. 重 ...

  3. 基础命名空间:序列化 System.Runtime.Serialization

    对象通常都有状态(state),从一个对象中抽取这种状态,不论是将它存储于某地,还是通过网络传送,这种抽取动作称为“将一个对象序列化”,而反向处理过程,从一个被序列化的状态重建一个对象即为反序列化. ...

  4. 格而知之6:我所理解的Runtime(1)

    基本简介 1.根据官方文档,OC有一个特性:它会尽可能把一些决定从编译时和链接时推迟到运行时才处理,所以这门语言需要的就不只是一个编译器,它还需要一个runtime系统来处理那些已经被编译过的代码. ...

  5. 对runtime的总结:让你会用Runtime

    导语Runtime,简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制,是一套底层的纯C语言的API,我们平时所编写的OC代码,在程序的运行过程中都转成了runtime的代码,平时调方 ...

  6. JSTL标签提示:"items" does not support runtime expressions

    今天在使用JSTL的 c:forEach 标签时,jsp提示:"items" does not support runtime expressions,后来才发现是因为taglib ...

  7. 找不到方法:“Boolean System.Runtime.Serialization.DataContractAttribute.get_IsReference()”的解决办法

    找不到方法:“Boolean System.Runtime.Serialization.DataContractAttribute.get_IsReference()”.的解决办法站点发布后部署到了两 ...

  8. 重写成员“log4net.Util.ReadOnlyPropertiesDictionary.GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)”时违反了继承安全性规则

    在.NET 4.0下使用最新版本的log4Net 1.2.10,会遇到下面这样的错误: 重写成员“log4net.Util.ReadOnlyPropertiesDictionary.GetObject ...

  9. System.Runtime.Serialization.SerializationException”类型的未经处理的异常在 System.Runtime.Serialization.dll 中发生

    异常信息: “System.Runtime.Serialization.SerializationException”类型的未经处理的异常在 System.Runtime.Serialization. ...

随机推荐

  1. linux c获取本地时间

    在标准C/C++中,我们可通过tm结构来获得日期和时间,tm结构在time.h中的定义如下: #ifndef _TM_DEFINED struct tm { int tm_sec; /* 秒–取值区间 ...

  2. java小爬虫

    爬取煎蛋网 1.找出页面网址的规律 2.设计页面图片网址的正则 代码: import java.io.BufferedInputStream; import java.io.BufferedOutpu ...

  3. CVE-2012-4969

    Microsoft Internet Explorer ‘CMshtmlEd::Exec’函数释放后使用漏洞(CNNVD-201209-394) Microsoft Internet Explorer ...

  4. VS2015的对象浏览器的使用

    用vs开发这么久了,还是第一次用上对象浏览器的功能,第一次用有一点懵逼,记录一下. 这个图标是项目 这是代表类,下面可以展开看到基类 在右边可以看到这个类的方法和成员 这个代表结构体 同样的右边显示成 ...

  5. log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader).

    一.异常描述: log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLo ...

  6. mysql 删除重复项

    DELETE FROM j_rank_rise_record WHERE id NOT IN ( SELECT id FROM ( SELECT * FROM j_rank_rise_record g ...

  7. sql developer连接mysql

    刚刚安装sql developer之后,数据库连接时没有mysql的选项,需要增加一个jar包 mysql-connector-java-6.0.5.zip 工具--->首选项--->数据 ...

  8. poj2387- Til the Cows Come Home(最短路)

    此为转载:http://blog.csdn.net/wangjian8006: 题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 解题思路: 模版题,这题要 ...

  9. 使用GNU工具链进行嵌入式裸机开发

    Embedded-Programming-with-the-GNU-Toolchain Vijay Kumar B. vijaykumar@bravegnu.org 翻译整理:thammer gith ...

  10. [leetcode trie]212. Word Search II

    Given a 2D board and a list of words from the dictionary, find all words in the board. Each word mus ...