C#的System.ICloneable接口說明


 

System.ICloneable接口支持克隆,即用與現有實例相同的值創建類的新實例。msdn上的解釋很簡單,主要就是clone方法的實行,介紹深拷貝和淺拷貝,搞的很糊塗,那么到底是什么意思呢?看看下面的原理可能就會明白很多了。引自http://www.cnblogs.com/anderslly/archive/2007/04/08/implementingcloneabletype.html

原理

如果我們有兩個值類型的變量,將其中一個變量的值賦給另一個,實際上會創建該值的一個副本,這個副本與原來的值沒有什么關系——這意味着改變其中一 個的值不會影響另一個變量的值。而如果是兩個引用類型的變量,其中一個變量的值賦給另一個的話(不包括string類型,CLR會對其有特殊處理),並沒 有創建值的副本,而是使兩個變量執行同一個對象——這意味着改變對象的值會同時影響兩個變量。要真正地創建引用類型的副本,我們必須克隆(clone)變量指向的對象

實現ICloneable接口使一個類型成為可克隆的(cloneable),這需要提供Clone方法來提供該類型的對象的副本。Clone方法不接受任何參數,返回object類型的對象(不管是何種類型實現該接口)。所以我們獲得副本后仍需要進行顯式地轉換。

實現ICloneable接口的方式取決於我們的類型的數據成員。如果類型僅包含值類型(int,byte等類型)和string類型的數據成員, 我們只要在Clone方法中初始化一個新的對象,將其的數據成員設置為當前對象的各個成員的值即可。事實上,object類的 MemberwiseClone方法會自動完成該過程。

如果自定義類型包含引用類型的數據成員,必須考慮Clone方法是實現淺拷貝(shallow copy)還是深拷貝(deep copy)。淺拷貝是指副本對象中的引用類型的數據成員與源對象的數據成員指向相同的對象。而如果是深拷貝,則必須創建整個對象的結構,副本對象中的引用類型的數據成員與源對象的數據成員指向不同的對象。

淺拷貝是容易實現的,就是使用前面提到的MemberwiseClone方法。開發人員往往希望使用的類型能夠實現深拷貝,但會發現這樣的類型並不 多。這種情況在System.Collections命名空間中尤其常見,這里面的類在其Clone方法中實現的都是淺拷貝。這么做主要出於兩個原因:

  1. 創建一個大對象的副本對性能影響較大;
  2. 通用的集合類型可能會包含各種各樣的對象,在這種情況下實現深拷貝並不可行,因為集合中的對象並非都是可克隆的,另外還存在循環引用的情況,這會讓深拷貝過程陷入死循環。

對於強類型的集合情況有所不同,因為它包含的元素是可控制的,此時深拷貝變得有用,同時也是可行的。例如System.Xml.XmlNode在其Clone方法中實現了深拷貝。

另外,如果需要克隆一個未實現ICloneable接口卻是可序列化的對象,通常可以通過序列化和反序列化來達到克隆的效果。但要小心,序列化過程不一定會序列化所以數據成員。

代碼

下面的代碼示例描述了克隆的各種方法。簡單的Employee類僅僅包含string和int類型的成員,所以使用object類型的 MemberwiseClone方法創建副本。Team類的Clone方法實現了深拷貝,它包含了一個Employee對象的集合,同時Team類提供了 一個私有的構造函數用以簡化Clone方法的代碼。構造函數的這種用法是簡化克隆過程的一種常見方式。

public class Employee : ICloneable
{
    public string Name;
    public string Title;
    public int Age;

    // Simple Emplyee constructor
    public Employee(string name, string title, int age)
    {
        Name = name;
        Title = title;
        Age = age;
    }

    public object Clone()
    {
        return MemberwiseClone();
    }

    public override string ToString()
    {
        return string.Format("{0} ({1}) - Age {2}", Name, Title, Age);
    }
}

Team類代碼:

public class Team : ICloneable
{
    public List<Employee> TeamMembers = new List<Employee>();

    public Team()
    {
    }

    private Team(List<Employee> members)
    {
        foreach (Employee e in members)
        {
            TeamMembers.Add(e.Clone() as Employee);
        }
    }

    // Adds an Employee object to the Team.
    public void AddMember(Employee member)
    {
        TeamMembers.Add(member);
    }

    // Override Object.ToString method to return a string representation of the team.
    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        foreach (Employee e in TeamMembers)
        {
            sb.AppendFormat("  {0}\r\n", e);
        }

        return sb.ToString();
    }

    // Implementation of ICloneable.Clone.
    public object Clone()
    {
        return new Team(this.TeamMembers);

        // the following code would create a shallow copy of the team.
        //return MemberwiseClone();
    }
}

測試代碼:

// Create the original team.
Team team = new Team();
team.AddMember(new Employee("Anders", "Developer", 26));
team.AddMember(new Employee("Bill", "Developer", 46));
team.AddMember(new Employee("Steve", "CEO", 36));

Team clone = team.Clone() as Team;

// Display the original team.
Console.WriteLine("Original Team:");
Console.WriteLine(team);

// Display the cloned team.
Console.WriteLine("Clone Team:");
Console.WriteLine(clone);

// Make changes.
Console.WriteLine("*** Make a change to original team ***");
Console.WriteLine(Environment.NewLine);
team.TeamMembers[0].Title = "PM";
team.TeamMembers[0].Age = 30;

// Display the original team.
Console.WriteLine("Original Team:");
Console.WriteLine(team);

// Display the cloned team.
Console.WriteLine("Clone Team:");
Console.WriteLine(clone);


免責聲明!

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



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