[討論] Serialize的問題
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
推
140.109.73.177 06/22, , 1F
140.109.73.177 06/22, 1F
推
140.125.251.33 06/22, , 2F
140.125.251.33 06/22, 2F
→
211.75.23.122 06/22, , 3F
211.75.23.122 06/22, 3F
→
211.75.23.122 06/22, , 4F
211.75.23.122 06/22, 4F
→
211.75.23.122 06/22, , 5F
211.75.23.122 06/22, 5F
推
140.125.251.33 06/23, , 6F
140.125.251.33 06/23, 6F
C_Sharp 近期熱門文章
PTT數位生活區 即時熱門文章