目前在做一个编辑器,编辑过程中有个保存编辑内容的实体类。因为内容比较复杂,为了防止意外的问题在保存的时候使用二进制序列化,但是这时就遇到一个问题,因为编辑需要这个实体类里面是有事件的,并且在编辑过程中这个事件已经被绑定到了用户控件。由于二进制序列化是精确还原对象的,所以尝试对这个实体类进行序列化的时候发生了问题:绑定了事件的用户控件是不可序列化的。
实体类部分代码如下:
1: [Serializable]
2: public class LabelItem
3: {
4: public event EventHandler TitleChanged;
5: public event EventHandler TextChanged;
6:
7: public event EventHandler PicChanged;
8:
9: //....其它代码一起实体类内容
10: }
11:
这个类在使用二进制序列化的时候,有可能你会遇到类似下面的错误:
错误信息为:
未处理的“System.Runtime.Serialization.SerializationException”类型的异常出现在 mscorlib.dll 中。
其他信息: 程序集“FSLib.OII.Editor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中的类型“FSLib.OII.Editor.Controls.LabelEditItem”未标记为可序列化。
究其原因,是因为LabelEditItem在编辑过程中绑定了LabelItem的几个事件以便于同步更新所有数据。但是它本身是个用户控件所以无法被序列化(当然我没加可序列化标记,但是我估计就算加了也是徒然)。
对于现在的问题,解决方案有两个:
1.抽象出实体类,去掉里面的事件等其它内容。对于事件等,则在继承的子类中编写。
2.在序列化的时候对它动手脚。
不要意思我很懒,所以第一个方法被排除了。因为这里面涉及了太多的类的改写动作,显然得不偿失。那下面从第二个解决方法下手(这里是事件,如果有其它导致无法正常序列化的内容时也可参考)。
用过序列化的同学都知道,序列化都有个 NonSerialized 的属性,这个属性用于标记在序列化过程中被忽略的字段。但是这个标记仅对字段起效,对于事件啊属性啊统统无效。经过测试,发现当事件没有被绑定(TitleChanged==null)的时候不会引发异常,那么我们就在这里下手脚吧。
对于序列化,有两个我们会用到的属性:OnSerializing 和 OnSerialized ,分别在开始序列化的时候、序列化完成的时候标记被调用。当然加这个属性的函数是有要求的,这个函数接受一个类型为 StreamingContext 的参数。与此相对应的,反序列化也会有对应的两步操作。
也许解决方案所有的同学都已经知道了:开始序列化的时候,将所有的事件都保存到已标记为NonSerialized 的字段中再清空事件;序列化完成后再还原事件,这样只要加两个函数,那所有的代码就都不用改了。
在本例中,代码如下(反序列化后,事件需要重新绑定的当然):
1: public class LabelItem
2: {
3: public event EventHandler TitleChanged;
4: public event EventHandler TextChanged;
5:
6: public event EventHandler PicChanged;
7:
8: [NonSerialized]
9: EventHandler _titleChanged;
10:
11: [NonSerialized]
12: EventHandler _textChanged;
13:
14: [NonSerialized]
15: EventHandler _picChanged;
16:
17: [OnSerializing]
18: void BeforeSerialize(StreamingContext ctx)
19: {
20: _titleChanged = _textChanged = _picChanged = null;
21:
22: if (TitleChanged != null) { _titleChanged = TitleChanged; TitleChanged = null; }
23: if (TextChanged != null) { _textChanged = TextChanged; TextChanged = null; }
24: if (PicChanged != null) { _picChanged = PicChanged; PicChanged = null; }
25: }
26:
27: [OnSerialized]
28: void AfterSerialize(StreamingContext ctx)
29: {
30: if (_titleChanged != null) TitleChanged = _titleChanged;
31: if (_textChanged != null) TextChanged = _titleChanged;
32: if (_picChanged != null) PicChanged = _titleChanged;
33: }
34: }
问题到这里已经解决了,但是喜欢举一反三的我们是否会联想到其它的用途呢?