C# class 淺拷貝 與 深拷貝


MemberwiseClone 方法創建一個淺表副本,具體來說就是創建一個新對象,然后將當前對象的非靜態字段復制到該新對象。
如果字段是值類型的,則對該字段執行逐位復制。如果字段是引用類型,則復制引用但不復制引用的對象;因此,原始對象及其復本引用同一對象。

為了實現深度復制,我們就必須遍歷有相互引用的對象構成的圖,並需要處理其中的循環引用結構。這無疑是十分復雜的。幸好借助.Net的序列化和反序 列化機制,可以十分簡單的深度Clone一個對象。原理很簡單,首先將對象序列化到內存流中,此時對象和對象引用的所用對象的狀態都被保存到內存 中。.Net的序列化機制會自動處理循環引用的情況。然后將內存流中的狀態信息反序列化到一個新的對象中。這樣一個對象的深度復制就完成了。在原型設計模 式中CLONE技術非常關鍵。

下面的代碼就是演示這個問題:

復制代碼
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace CloneDemo
{
[Serializable]
class DemoClass
{
    public int i = 0;
    public int[] iArr = { 1, 2, 3 };
 
    public DemoClass Clone1() //淺CLONE
    {
        return this.MemberwiseClone() as DemoClass;
    }
 
    public DemoClass Clone2() //深clone
    {
        MemoryStream stream = new MemoryStream();
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, this);
        stream.Position = 0;
        return formatter.Deserialize(stream) as DemoClass;
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        DemoClass a = new DemoClass();
        a.i = 10;
        a.iArr = new int[] { 8, 9, 10 };
        DemoClass b = a.Clone1();
        DemoClass c = a.Clone2();
 
        // 更改 a 對象的iArr[0], 導致 b 對象的iArr[0] 也發生了變化 而 c不會變化
          a.iArr[0] = 88;
 
        Console.WriteLine("MemberwiseClone");
        Console.WriteLine(b.i);
        foreach (var item in b.iArr)
        {
            Console.WriteLine(item);
        }
 
        Console.WriteLine("Clone2");
        Console.WriteLine(c.i);
        foreach (var item in c.iArr)
        {
            Console.WriteLine(item);
        }
 
        Console.ReadLine();
    }
}
}
復制代碼

另外一個例子是針對數組,C#中的數組是引用型的變量,我們通過數組來進行演示: 
淺拷貝

復制代碼
using System;

class ShallowCopy : ICloneable
{
public int[] v = {1,2,3};

public Object Clone()
{
return this.MemberwiseClone();
}

public void Display()
{
foreach(int i in v)
Console.Write( i + ", ");
Console.WriteLine();
}
}

class Client
{
public static void Main()
{

ShallowCopy sc1 = new ShallowCopy();
ShallowCopy sc2 = (ShallowCopy)sc1.Clone();
sc1.v[0] = 9;
 
sc1.Display();
sc2.Display();

}
}
復制代碼

ShallowCopy對象實現了一個淺拷貝,因此當對sc1進行克隆時,其字段v並沒有克隆,這導致sc1與sc2的字段v都指向了同一個v,因此,當修改了sc1的v[0]后,sc2的v[0]也發生了變化。

深拷貝:

復制代碼
using System;

class DeepCopy : ICloneable
{
public int[] v = {1,2,3};

// 默認構造函數
public DeepCopy()
{
}

// 供Clone方法調用的私有構造函數
private DeepCopy(int[] v)
{
this.v = (int[])v.Clone();
}

public Object Clone()
{
// 構造一個新的DeepCopy對象,構造參數為
// 原有對象中使用的 v
return new DeepCopy(this.v);
}

public void Display()
{
foreach(int i in v)
Console.Write( i + ", ");
Console.WriteLine();

}
}

class Client
{
public static void Main()
{
DeepCopy dc1 = new DeepCopy();
DeepCopy dc2 = (DeepCopy)dc1.Clone();
dc1.v[0] = 9;
 
dc1.Display();
dc2.Display();

}
}
復制代碼

這次在克隆的時候,不但克隆對象本身,連里面的數組字段一並克隆。因此,最終打印出來的dc1與dc2不同。

當然我們也可以建個深拷貝的幫助類:

復制代碼
public static class ObjectCopier
{
    
/// <summary>
/// Perform a deep Copy of the object.
/// </summary>
/// <typeparam name="T">The type of object being copied.</typeparam>
/// <param name="source">The object instance to copy.</param>
/// <returns>The copied object.</returns>
public static T Clone<T>(T source)
{
    if (!typeof(T).IsSerializable)
    {
        throw new ArgumentException("The type must be serializable.", "source");
    }
 
    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }
 
    IFormatter formatter = new BinaryFormatter();
    Stream stream = new MemoryStream();
    using (stream)
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}

}
復制代碼
轉自:https://www.cnblogs.com/MRRAOBX/articles/4084171.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM