遇到這麼個問題,沒有參考任何資料,自己想了個方法
對於自訂的一些簡單類型還好,遇到.Net裡一些複雜的類就無能為力了,不知道還有什麼更好的方法。
class CsToD
{
//基本思想是:一個對象所佔據的記憶體空間,取決於它的執行個體欄位(包括繼承樹上的私人執行個體欄位)
public T DeepCloneObject<T>(T obj) where T : class
{
//System.String類型似乎比較特殊,複製它的所有欄位,並不能複製它本身
//不過由於System.String的不可變性,即使指向同一對象,也無所謂
//而且.NET裡本來就用字串池來維持
if (obj == null || obj.GetType() == typeof(string))
return obj;
object newObj = null;
try
{
//嘗試調用預設建構函式
newObj = Activator.CreateInstance(obj.GetType());
}
catch
{
//失敗的話,只好枚舉建構函式了
foreach (ConstructorInfo ci in obj.GetType().GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
{
try
{
ParameterInfo[] pis = ci.GetParameters();
object[] objs = new object[pis.Length];
for (int i = 0; i < pis.Length; i++)
{
if (pis[i].ParameterType.IsValueType)
objs[i] = Activator.CreateInstance(pis[i].ParameterType);
else
//參數類型可能是抽象類別或介面,難以執行個體化
//我能想到的就是枚舉應用程式定義域裡的程式集,找到實現了該抽象類別或介面的類
//但顯然過於複雜了
objs[i] = null;
}
newObj = ci.Invoke(objs);
//無論調用哪個建構函式,只要成功就行了
break;
}
catch
{
}
}
}
foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
fi.SetValue(newObj, fi.GetValue(obj));
else
fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
}
//基類的私人執行個體欄位在子類裡檢索不到,但它仍佔據子類對象的記憶體空間
Deep(newObj, obj);
return (T)newObj;
}
//複製繼承樹上的私人執行個體欄位
public void Deep(object newObj, object obj)
{
for (Type father = newObj.GetType().BaseType; father != typeof(object); father = father.BaseType)
{
foreach (FieldInfo fi in father.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
//只需要處理私人欄位,因為非私人成員已經在子類處理過了
if (fi.IsPrivate)
{
if (fi.FieldType.IsValueType || fi.FieldType == typeof(string))
{
fi.SetValue(newObj, fi.GetValue(obj));
}
else
{
fi.SetValue(newObj, DeepCloneObject(fi.GetValue(obj)));
}
}
}
}
}
}
寫個代碼來測試一下:
class Program
{
static void Main()
{
Data3 data3 = new Data3();
Data data = new Data(data3);
data.PriValue = "Pri";
data.Integer = 3;
//修改cloneData對象,並未影響到data對象,說明複製成功
CsToD ctd = new CsToD();
Data cloneData = ctd.DeepCloneObject(data);
cloneData.Integer = 1000;
cloneData.PriValue = "clone";
cloneData.Value.D3String = "clonestr";
Console.WriteLine(data.Integer + "<->" + cloneData.Integer);
Console.WriteLine(data.PriValue + "<->" + cloneData.PriValue);
Console.WriteLine(data.Value.D3String + "<->" + cloneData.Value.D3String);
//而如果修改refData對象,data對象也被改變,說明是同一對象
Data refData = data;
refData.Integer = 555;
refData.PriValue = "ref";
refData.Value.D3String = "refstr";
Console.WriteLine(data.Integer + "<->" + refData.Integer);
Console.WriteLine(data.PriValue + "<->" + refData.PriValue);
Console.WriteLine(data.Value.D3String + "<->" + refData.Value.D3String);
}
}
class Data:Data2
{
public Data(Data3 value)
{
this.Value = value;
}
public Data() { }
public Data3 Value;
}
class Data2
{
public Data2(int value)
{
this.Integer = value;
}
public string PriValue
{
get
{
return priValue;
}
set
{
priValue = value;
}
}
public Data2() { }
public int Integer;
private string priValue;
}
class Data3
{
public Data3()
{
D3String = "D3";
d3Integer = 3;
}
public string D3String;
private int d3Integer;
}