【问题标题】:What is the method MemberwiseClone() doing?MemberwiseClone() 方法是做什么的?
【发布时间】:2011-01-18 08:47:30
【问题描述】:

我对下面的这段代码感到困惑,

Developer devCopy = (Developer)dev.Clone();

Developer 类的 Clone 方法只是创建了一个 Employee 克隆,那么开发者如何获得另一个开发者的克隆。

public abstract class Employee
{
    public abstract Employee Clone();

    public string Name { get; set; }
    public string Role { get; set; }
}


public class Typist : Employee
{
    public int WordsPerMinute { get; set; }

    public override Employee Clone()
    {
        return (Employee)MemberwiseClone();
    }

    public override string ToString()
    {
        return string.Format("{0} - {1} - {2}wpm", Name, Role, WordsPerMinute);
    }
}


public class Developer : Employee
{
    public string PreferredLanguage { get; set; }

    public override Employee Clone()
    {
        return (Employee)MemberwiseClone();
    }

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


Developer dev = new Developer();
dev.Name = "Bob";
dev.Role = "Team Leader";
dev.PreferredLanguage = "C#";

Developer devCopy = (Developer)dev.Clone();
devCopy.Name = "Sue";

Console.WriteLine(dev);
Console.WriteLine(devCopy);

/* OUTPUT

Bob - Team Leader - C#
Sue - Team Leader - C#

*/

Typist typist = new Typist();
typist.Name = "Kay";
typist.Role = "Typist";
typist.WordsPerMinute = 120;

Typist typistCopy = (Typist)typist.Clone();
typistCopy.Name = "Tim";
typistCopy.WordsPerMinute = 115;

Console.WriteLine(typist);
Console.WriteLine(typistCopy);

/* OUTPUT

Kay - Typist - 120wpm
Tim - Typist - 115wpm

*/

【问题讨论】:

    标签: c# design-patterns prototype clone


    【解决方案1】:

    有两种复制类型:ShallowCopyDeepCopy

    ShallowCopy 复制所有value type 成员和nonstatic 字段,这正是MemberwiseClone() 所做的。

    但是reference type 值呢?这是使用DeepCopy 的地方。通过使用DeepCopy,不会复制引用,但会从引用生成一个新对象。

    注意,通过使用ShallowCopy,引用地址被复制,所以复制的引用地址指向同一个对象。因此,更改一个对象会更改所有副本。

    考虑以下示例:

    class Person
    {
        public int Age { get; set; }
        public string Name { get; set; }
        public DateTime BirthDate { get; set; }
        public IdInfo IdInfo { get; set; }
    
        public Person ShallowCopy()
        {
            return this.MemberwiseClone() as Person;
        }
    
        public Person DeepCopy()
        {
            var clone = this.MemberwiseClone() as Person;
            clone.Name = String.Copy(Name);
            clone.IdInfo = new IdInfo(IdInfo.IdNumber);
    
            return clone;
        }
    }
    
    class IdInfo
    {
        public int IdNumber { get; set; }
        public IdInfo(int idNumber)
        {
            this.IdNumber = idNumber;
        }
    }
    

    【讨论】:

      【解决方案2】:

      因为MemberwiseClone() 方法正在为您执行此操作。见the documentation

      MemberwiseClone 方法通过创建一个新对象来创建浅拷贝,然后将当前对象的非静态字段复制到新对象。如果字段是值类型,则执行该字段的逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象和它的克隆对象引用的是同一个对象。

      当你看到一个你不理解的方法时,你可以追踪谁声明了它(我猜是在 Visual Studio 中),然后查看它的文档。这让事情在大多数时候都很明显。

      【讨论】:

      • 这通常是您需要克隆的全部内容。原始字段是孤立的副本 - 肯定是克隆的。在源对象中不会注意到更改 MemberwiseClone 的原始字段。对象引用字段是相同的。指向另一个对象不会影响另一个对象。但是,在 MemberwiseCloning 对象引用之后直接指向同一个对象。克隆只发生在直接对象级别,它不是递归过程。根据我的经验,需要递归深度克隆并不常见,因此我通常不会费心创建深度克隆函数。
      【解决方案3】:

      函数MemberwiseClone 创建一个新对象,其字段是原始结构中的逐位副本。它是任何允许在不使用反射或序列化的情况下进行克隆的可继承类的必要部分,但它只是整个难题的一小部分。

      如果您希望允许在可继承类中进行克隆,您应该定义一个protected virtual T BaseClone<T>() 克隆方法;从Object 继承的基级类应该调用base.MemberwiseClone;所有其他类都应该使用base.BaseClone<T> 来获取新实例,然后用原始对象中的那些的克隆替换任何可变的可克隆字段。

      我还建议定义以下接口:

      interface ISelf<out T> {T Self();}
      interface ICloneable<out T> : ISelf<T> {T Clone();}
      

      这将允许一个类可能有一些可以克隆的后代和一些不能克隆的情况。那些可以被克隆的可以公开公共克隆方法(应该链接到BaseClone&lt;theirOwnType&gt;)。需要基类型的可克隆衍生物的方法可以使用ICloneable&lt;theBaseType&gt;类型的参数;这将允许它们接受基类型的任何可克隆派生类,即使并非所有此类派生类都共享一个公共基类。

      【讨论】:

        猜你喜欢
        • 2018-03-20
        • 1970-01-01
        • 2023-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多