C#对象克隆介绍

浅拷贝和深拷贝

有两种对象克隆的方法:浅拷贝和深拷贝。浅拷贝只是复制引用,而不会复制引用的对象。深拷贝会复制引用的对象。

因此,原始对象中的引用和浅拷贝对象中的同一个引用都指向同一个对象。而深拷贝的对象包含了对象的一切直接或间接的引用。参看维基百科(http://en.wikipedia.org/wiki/Object_copy)来获得更多解释。

ICloneable接口

ICloneable接口包含一个Clone方法,可以用来创建当前对象的拷贝。

public interface ICloneable
{
object Clone();
}

ICloneable的问题是Clone方法并不会显式地指定是执行浅拷贝或深拷贝,因此调用者将无法确定实际情况。因此,有一些关于把ICloneable从.NET框架中淘汰的讨论。MSDN文档似乎暗示Clone方法是进行的深拷贝,但是文档没有明确的说明:

ICloneable接口包含一个成员方法,Clone,意在支持超过MemberWiseClone所提供的功能... MemberWiseClone进行的是浅拷贝...

类型安全的克隆

ICloneable的另一个缺点是Clone方法返回的是一个对象,因此每次调用Clone都要进行一次强制类型转换。

Person joe = new Person();
joe.Name = "Joe Smith";
Person joeClone = (Person)joe.Clone();

一种可以避免进行强制类型转换的方式是提供你自己的类型安全的Clone方法。注意,你依然要提供ICloneable.Clone方法的以满足iCloneable接口的要求。

public class Person : ICloneable
{
public string Name;
object ICloneable.Clone()
{
return this.Clone();
}
public Person Clone()
{
return (Person)this.MemberwiseClone();
}
}

MemberwiseClone() 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

选择克隆方法

1. 手工克隆

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

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;
}
}

2. 使用MemberWiseClone方法

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

public class Person : ICloneable
{
public string Name;
public Person Spouse;
public object Clone()
{
return this.MemberwiseClone();
}
}

3. 用反射进行克隆

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

4. 使用序列化进行克隆

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

序列化克隆的一种   System.SerializableAttribute

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Sample

{

[Serializable]

public class Person : ICloneable
{

  public string Name;
  object ICloneable.Clone()
  {
  return this.Clone();
  }
  public Person Clone()
  {

    using(MemoryStream ms = new MemoryStream())

    {
      object cloneObject;
      BinaryFormatter bf = new BinaryFormatter(null,new StreamingContext(StreamingContextStates.Clone));
      bf.Serialize(ms,this);
      ms.Seek(0,SeekOrigin.Begin);

      cloneObject = bf.Deserialize(ms);
      ms.Close();
      return (Person)cloneObject;
    }

  }

}

}

5. 使用IL进行克隆

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

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

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

时间: 2024-10-13 01:28:06

C#对象克隆介绍的相关文章

Java提高篇——对象克隆(复制)

阅读目录 为什么要克隆?如何实现克隆浅克隆和深克隆解决多层克隆问题总结 假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况. 但是如果你复制的是一个对象,情况就有些复杂了. 假设说我是一个beginner,我会这样写: class Student { private int number; pu

Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

Java对象克隆(Clone)及Cloneable接口.Serializable接口的深入探讨 Part I 没啥好说的,直接开始Part II吧. Part II 谈到了对象的克隆,就不得不说为什么要对对象进行克隆.Java中所有的对象都是保存在堆中,而堆是供全局共享的.也就是说,如果同一个Java程序的不同方法,只要能拿到某个对象的引用,引用者就可以随意的修改对象的内部数据(前提是这个对象的内部数据通过get/set方法曝露出来).有的时候,我们编写的代码想让调用者只获得该对象的一个拷贝(也

C# 对象克隆,DataTable转LIST

public class ConvertHelper<T> where T : new() { private static string module = "ConvertHelper.cs"; public static ObservableCollection<T> ConvertToList(List<T> listobject) { ObservableCollection<T> collection = null; try {

[Java学习笔记]对象克隆

对象克隆: 浅克隆: 被克隆的对象中的值类型数据被复制一份新值,但是引用的对象只被复制其引用传递给新克隆出来的对象 深克隆: 被克隆的对象中的值类型数据被复制一份新值,并且其中引用的对象会重新创建新的对象,把新对象的引用传递给新克隆出来的对象 Java中实现克隆: Java中Object中定义了clone方法,默认为浅克隆操作,即你只是简单super.clone得到的结果是浅克隆的结果,如果需要深克隆,则需要实现Cloneable接口,并且重写clone方法 查看Java源代码发现Object中

json字符串与json对象简单介绍

json字符串与json对象简单介绍:本章节介绍一下标题两个称呼所指的内容是什么.在网上或者一些前端群里,经常会看到json字符串或者json对象两个称呼.关于json字符串大家几乎没有什么异议,那就是一个字符串,具有一定的格式,代码如下: var web = '{ "name": "蚂蚁部落", "url": "www.softwhy.com" }'; 下面就该到了json对象这个称呼了,先看一个代码实例: var obj

jQuery对象入门级介绍

你是否曾经见过像  $(".cta").click(function(){})这样的JavaScrip代码?或许你还会思考下 $('#X') 是什么,如果看到这些你都觉得摸不着头脑,那请一定要读完这篇文章.如果你觉得上述的代码片段是不能正常工作的,那请先看一些jQuery的代码范例,你会发现链接中的代码都是这样的结构. 这篇文章将会分析下面代码片段(动画化一个方形)中出现的一些关键知识点.你可能不会经常接触这样的代码,但了解一下这段代码的机制有助于你理解jQuery: 1 2 3 4

关于document.selection和TextRange对象的介绍

document.selection只有IE支持 window.getSelection()也只有FireFox和Safari支持,都不是标准语法. selection 对象代表了当前激活选中区,即高亮文本块,或文档中用户可执行某些操作的其它元素.selection 对象的典型用途是作为用户的输入,以便识别正在对文档的哪一部分正在处理,或者作为某一操作的结果输出给用户. 用户和脚本都可以创建选中区.用户创建选中区的办法是拖曳文档的一部分.脚本创建选中区的办法是在文本区域或类似对象上调用 sele

【转】JSP内置对象详细介绍(下)

第四个问题:Servlet的生命周期是什么呢? Servlet的生命周期,简单的概括分为四步:servlet类加载->实例化->服务->销毁.Servlet实例化的步骤: 1.Servlet容器启动时:读取Web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法. 2.在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会 判断内存中是否

【转】JSP内置对象详细介绍(上)

第一个问题: JSP有多少类内置对象? 要想回答好这个问题,首先是对JSP的基本知识和技术有深刻的理解和掌握.主要有九个 内置对象:application对象,config对象,exception对象,out对象,page对象,pageContext对象,request对象,reponse对象,session对象. 第二个问题:JSP的内置对象都是什么类型的?这些对象的作用是什么?它们常用的方法是什么呢? (1)application对象:javax.servlet.ServletContext