作者psvsps2 (Op颖)
看板C_Sharp
标题[讨论] Serialize的问题
时间Tue Jun 21 23:31:29 2005
Serialization 或称 Persistance 是一种将物件保存的机制,透过这套机制
可以将物件保存在档案中,再需要时再还原物件的状态。
.NET有直接支援Serialize,任何class只要标记[Serializable]就具备了
Serialize的能力,所有的Fiels(public, private, protected, static..)
都会在程式呼叫IFormatter.Serialize()的时候被保存下来,在程式呼叫
IFormatter.Deserialize()时从stream中建立class的instance。
关於这些基本的概念MSDN文件上都有相当详细的说明,这边就简单的带过
[Serializable] SerializableAttribute class的简写
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct |
AttributeTargets.Enum | AttributeTargets.Delegate, Inherited=false)]
public sealed class SerializableAttribute : Attribute
由上面的宣告我们可以知道[Serializable]对象为
1.Class
2.Struct
3.Enum
4.Delegate
且没有继承的效果(Inherited = false),这点很重要稍後会讨论。
一般而言标示了[Serializable]通常不用任何处理就可以完成大部分
Serialize的工作了。
[NonSerialized] NonSerializedAttribute class的简写用来指示Field不用
在执行Serialize时存到stream中。
ISerializable 用来实做自定的Serialize动作请直接参考MSDN的范例
在这边要讨论的是小弟不久前遇到的一个问题,首先把焦点放到之前提过的
[Serializable]这个属性,由於这个属性不具备继承的能力(
AttributeUsageAttribute(...,Inherited = false) )
所以下面的程式码会有问题
class MyBase
{
....
}
[Serializable]
class MyDerived : MyBase, ISerializable
{
public MyDerived(){}
public virtual void GetObjectData(SerializationInfo info, StreamingContext
context)
{
.......
//处理MyDerived的Serialize动作
base.GateObjectData(info,context); //处理base的serialize
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这一行会丢出SerializationException因为base没有[Serializable]
}
protected MyDerived(SerializationInfo info,
StreamingContext context) : base(info,context)
^^^^^^^^^throw Exception
{
...
//处理deserialize的动作
}
}
有几种方法可以解决
1.把程式码中串联到base的serialize动作拿掉
(base.GateObjectData(info,context); or
base(info,context))
但是假如哪天base又变成需要serialize是不是我们又要把这些被拿掉的程式码
加回去?
2.将base也标上[Serializable]
问题是第一如果base真的不需要serialize这显然会混淆其他的工程师。
第二 假如base我们没有source code怎办?叫提供base的厂商给我们加上
後再寄来?
上面两种解法都会造成我门开发的class和base再处理serialize上的偶合(coupling)
也就是我们没有办法在base变更serialize的支援时不改变继承体系其他的class程式。
程式有越多的偶合就越难维护,这当然要避免,这边提供一个例子
class GenericBaseSerialize
{
public static void SerializeBase(SerializationInfo info,
StreamingContext context, object obj)
{
Type baseType = obj.GetType().BaseType;
//当baseType为object时结束deserialize
while(baseType != typeof(object))
{
BindingFlag flag = BindingFlags.Instance |
BindingFlags.DeclaredOnly |
BindingFlags.NonPublic |
BindingFlags.Public;
//取得所有base type 的 Fields
FieldInfo[] fields = baseType.GetFields(flag);
foreach (FieldInfo field in fields)
{
if(field.IsNotSerialized)
{
//field不需要serialize继续下一个field
continue;
}
string sToken = type.Name + field.Name;
info.AddValue(sToken, field.GetValue(obj));
}
//继续处理base type
baseType = baseType.BastType;
}
}
public static void DeSerializeBase(SerializationInfo info,
StreamingContext context, object obj)
{
Type baseType = obj.GetType().BaseType;
//当baseType为object时结束deserialize
while(baseType != typeof(object))
{
BindingFlag flag = BindingFlags.Instance |
BindingFlags.DeclaredOnly |
BindingFlags.NonPublic |
BindingFlags.Public;
//取得所有base type 的 Fields
FieldInfo[] fields = baseType.GetFields(flag);
foreach (FieldInfo field in fields)
{
if(field.IsNotSerialized)
{
//field不需要serialize继续下一个field
continue;
}
string sToken = type.Name + field.Name;
//取出field的值(deserialize)
object value = info.GetValue(sToken,field.FieldType);
//将取出的值设定到obj中的field
field.SetValue(obj, value);
}
//继续处理base type
baseType = baseType.BastType;
}
}
}
[Serializable]
class MyDerived : MyBase, ISerializable
{
public MyDerived(){}
public virtual void GetObjectData(SerializationInfo info, StreamingContext
context)
{
.......
//处理MyDerived的Serialize动作
//处理base的serialize动作
GenericBaseSerialize.SerializeBase(info,context,this);
}
protected MyDerived(SerializationInfo info,
StreamingContext context)
{
...
//处理deserialize的动作
//处理base的serialize动作
GenericBaseSerialize.DeSerializeBase(info,context,this);
}
}
完全不用再花心力去管base是否有支援[Serializable]了。
不过这个做法有一个缺点就是Reflection的动作实在是很慢,大量物件还是尽量避免
...毕竟弹性和效率总是两难全...
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 61.229.16.186
1F:推 seagal:头推 p兄的文章每次都很有深度 140.109.73.177 06/22
2F:推 sheauren:不考虑空间问题 我是都把object转成xml储存 140.125.251.33 06/22
3F:→ psvsps2:小弟说的大量物件是可能程式中会频繁建立一个 211.75.23.122 06/22
4F:→ psvsps2:物件非常多次,如果这种情形每次serialize时 211.75.23.122 06/22
5F:→ psvsps2:都用上面方法对效率影响颇大 211.75.23.122 06/22
6F:推 sheauren:之前是因为传递WebService的时候使用到:D 140.125.251.33 06/23