C# Clone() 方法

Clone 分浅拷贝和深拷贝

两者的区别:当有引用类型成员时,浅拷贝复制的是成员的引用,深拷贝复制的是成员对象。

如何实现对象的拷贝功能:

== 继承接口ICloneable实现Clone方法

== ICloneable.Clone本身并不能区分(Deep or Shallow)你可以在Clone中调用MemberwiseClone来实现一个ShallowClone也可以自己来实现一个DeepClone。不过按照microsoft的建议当实现ICloneable的时候是准备用来实现一个DeepClone。
== ICloneable.Clone返回类型是object
== 其中MemberwiseClone的复制原理是值类型按位复制,引用类型复制对象的引用。这里有一个要注意的就是String类型,虽然是引用类型,不过在这里表现上和值类型是一样的,在Clone的时候就当作值类型来看待好了。
我的习惯是自己从ICloneable派生一个接口
  object DeepClone();
  object ShallowClone();
}
public class Person : ICloneable
{
public string Name;
public Person Spouse;
public object Clone()
{
Person p = new Person();
p.Name = this.Name;
if (this.Spouse != null)
p.Spouse = (Person)this.Spouse.Clone();
return p;
}
}
public class Person : ICloneable
{
public string Name;
public Person Spouse;
public object Clone()
{
return this.MemberwiseClone();
}
}


interface IObjectCloneable : ICloneable{

实现Clone的方法:

 

1. 手工克隆

一个能够保证对象完全按照你所想的那样进行克隆的方式是手工克隆对象的每一个域(field)。这种方式的缺点是麻烦而且容易出错:如果你在类中增加了一个域,你很可能会忘记更新Clone方法。还要在克隆引用对象指向原始对象的时候,注意避免无限循环引用。下面是一个进行深拷贝的简单例子:

2. 使用MemberWiseClone方法

MemberWiseClone是Object类的受保护方法,能够通过创建一个新对象,并把所有当前对象中的非静态域复制到新对象中,从而创建一个浅拷贝。对于值类型的域,进行的是按位拷贝。对于引用类型的域,引用会被赋值而引用的对象则不会。因此,原始对象及其克隆都会引用同一个对象。注意,这种方法对派生类都是有效的,也就是说,你只需在基类中定义一次Clone方法。下面是一个简单的例子:

3. 用反射进行克隆

用反射进行克隆是使用Activator.CreateInstance方法来创建一个相同类型的新对象,然后用反射对所有域进行浅拷贝。这种方法的优点是它是全自动的,不需要在对象中添加或删除成员的时候修改克隆方法。另外它也能被写成提供深拷贝的方法。缺点是使用了反射,因此会比较慢,而且在部分受信任的环境中是不可用的。示例代码

4. 使用序列化进行克隆

克隆一个对象的最简单的方法是将它序列化并立刻反序列化为一个新对象。和反射方法一样,序列化方法是自动的,无需在对对象成员进行增删的时候做出修改。缺点是序列化比其他方法慢,甚至比用反射还慢,所有引用的对象都必须是可序列化的(Serializable)。另外,取决于你所使用的序列化的类型(XML,SOAP,二进制)的不同,私有成员可能不能像期望的那样被克隆。示例代码在这里这里这里

5. 使用IL进行克隆

一种罕见的解决方案是使用IL(中间语言)来进行对象克隆。这种方式创建一个动态方法(DynamicMethod),获取中间语言生成器(ILGenerator),向方法中注入代码,把它编译成一个委托,然后执行这个委托。委托会被缓存,因此中间语言只在初次克隆的时候才会生成,后续的克隆都不会重新生成一遍。尽管这种方法比使用反射快,但是这种方法难以理解和维护。示例代码

6. 使用扩展方法进行克隆

Havard Stranden用扩展方法(extention method)创建了一个自定义的克隆框架。这个框架能够创建对象及其引用的对象的深拷贝,不管对象结构有多复杂。缺点是,这是一个不提供源代码的自定义框架(更新:现在已经包括源代码了,参见本文评论),并且它不能在不使用无参数构造器的时候,拷贝由私有方法创建的对象。另一个问题,也是所有自动化的深克隆方法共有的问题是,深拷贝通常需要灵活地处理不能进行简单自动化特殊情况(例如未受管理的资源)。



C# Clone() 方法
浅复制:复制顶层(top-level)对象
深复制:复制对象及子对象
class DrawBase:System.Object.ICloneable
       {
               public string name = "jmj";
               public DrawBase()
               {
               }
                //注意:重写接口中的方法,不能使用override关键字
               public object Clone()
               {
                       return this as object;                    //引用同一个对象
                       return this.MemberwiseClone();    //浅复制
(在C#中可以使用MemberwiseClone()方法来实现浅copy在C#中可以使用MemberwiseClone()方法来实现浅copy)
                        return new DrawBase() as object;  //深复制
               }
       }
class Program
{
 
               static void Main(string[] args)
               {
                       DrawBase rect = new DrawBase();
                       Console.WriteLine(rect.name);
                       DrawBase line = rect.Clone() as DrawBase;
                       line.name = "a9fs3";
                       Console.WriteLine(rect.name);
                       DrawBase ploy = line.Clone() as DrawBase;
                       ploy.name = "lj";
                       Console.WriteLine(rect.name);
 
                       Console.WriteLine(object.ReferenceEquals(line, ploy));
                       Console.ReadLine();
               }
}
运行结果:
 return this as object;      //引用同一个对象
输出:   jmj
               a9fs3
               lj
               True
 
return this.MemberwiseClone(); //浅复制
return new DrawBase() as object;//深复制
输出均为:       jmj
                         jmj
                         jmj
                         False
解释:
return this as object  方法总是引用同一个对象,因此相应的堆内存上的值会改变!
后两种方法都是对对象的复制,区别在于复制的类别不同:深复制会复制整个填充的对象,包括该对象中其他引用类型和值类型的值;而浅复制只复制了一个对象中所有引用,它没有 的复制,通过引用它们的其他对象的引用来共享它们。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值