對象的深層拷貝

來源:互聯網
上載者:User

遇到這麼個問題,沒有參考任何資料,自己想了個方法

對於自訂的一些簡單類型還好,遇到.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;
}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.