class Person
{
public string name;
public List<string> hobby;
} void main()
{
Person p1 = new Person();
p1.name = "tom";
p1.hobby = new List<string>(){"eat","sleep"};
Person p2 = p1;
}

当p1=null时,p2的name和hobby值是还存在的, p1=null的操作其实如下的红色叉号一样,取消引用,并不影响p2:

1、值类型与引用类型以及区别(知识储备)

  1. 值类型(Value Type)(如 char、int 和 float)、枚举类型和结构类型。
  2. 引用类型(Reference Type) 包括类 (Class) 类型、接口类型、委托类型和数组类型。
  • 值类型的变量直接包含其数据,
  • 引用类型的变量则存储对象引用。
  • 对于引用类型,两个变量可能引用同一个对象,因此对一个变量的操作可能影响另一个变量所引用的对象。对于值类型,每个变量都有自己的数据副本,对一个变量的操作不可能影响另一个变量。

2、堆栈(知识储备)

  • 堆栈(stack)是一种先进先出的数据结构,在内存中,变量会被分配在堆栈上来进行操作。
  • 堆(heap)是用于为类型实例(对象)分配空间的内存区域,在堆上创建一个对象,会将对象的地址传给堆栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)。

3、拷贝的概念

a.浅拷贝(Shallow Copy影子克隆):只复制对象的基本类型,对象类型,仍属于原来的引用。(即引用成员的克隆只克隆成员的地址,不克隆地址所指向的在堆上的分配的内存对象)
      b.深拷贝(Deep Copy 深度克隆):不仅复制对象的基本类,同时也复制原对象中的对象.完全产生新对象。(相当于完全new出一个新的对象,只不过子对象的成员已克隆有值了)

4、克隆存在的必要性

我认为当设置一个对象的状态要付出昂贵的代价,并且又需要取得该对象的一个拷贝以便改变当前的一些状态时,克隆就显得十分必要。下面列举一个刚好能体现我刚刚所说的情况的例子。 就拿 DataTable 类来说吧。建立一个 DataTable 会包含诸如以下的操作:为取得架构和数据而查询数据库、添加约束、设置主键等等。那么,当需要该 DataTable 的一个新的拷贝,哪怕是对架构作极小的改变或添加新的一行记录等等, 明智的选择会是克隆已存在的对象再对其进行操作,而不是创建一个新的DataTable, 那样将需要更多的时间和资源。克隆也广泛应用于数组和集合,这些时候往往会多次需要已存在对象的一个拷贝。

5、深拷贝和浅拷贝值成员和引用成员的拷贝方式

2.1 对于值类型:
          a.浅拷贝: 对值类型字段只是简单的拷贝一个副本到目标对象,改变目标对象中值类型字段的值不会反映到原始对象中,因为拷贝的是副本

b.深拷贝: 通过赋值等操作直接实现,将对象中的值类型的字段拷贝到新的对象中。   和浅拷贝相同

2.2 对于引用类型:
          a.浅拷贝: MemberwiseClone 方法创建一个浅副本,方法是创建一个新对象,如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用原始对象,与原对象引用同一对象。  指的是拷贝他的一个引用(内存地址)到目标对象。改变目标对象中引用类型字段的值它将反映到原始对象中,因为拷贝的是指向堆是上的一个地址

b.深拷贝:拷贝对象应用,也拷贝对象实际内容,也就是创建了一个新的改变新对象 不会影响到原始对象的内容,这种情况需要为其实现ICloneable接口中提供的Clone方法。

6、两者的差别

a. 差别就是在对于引用类型的实现深拷贝和浅拷贝的时候的机制不同,前者是MemberwiseClone 方法实现,后者是通过继承实现ICloneable接口中提供的Clone方法,实现对象的深拷贝。

b. 深拷贝与浅拷贝不同的是对于引用拷贝的处理,深拷贝将会在新对象中创建和原是对象中对应值类型的字段并且赋值。浅拷贝不会创建新引用类型,会返回相同的类型引用。深拷贝会重新创建新对象,返回新对象的引用!

7、代码的实现

深度拷贝

  class Program
{
static void Main(string[] args)
{
ShallowCopy obj1 = new ShallowCopy();
obj1.Name = "Tom";
ShallowCopy obj2 = (ShallowCopy)obj1.Clone();
obj1.Name = "Jerry";
obj1.list[] = ;
obj1.Display();
obj2.Display();
Console.WriteLine(object.ReferenceEquals(obj1, obj2));
Console.ReadKey();
}
}
[Serializable]
public class ShallowCopy : ICloneable
{
public List<int> list = new List<int>() { , , , };
public string Name { get; set; } public Object Clone()
{
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = ;
ShallowCopy obj = (ShallowCopy)formatter.Deserialize(memoryStream);
return obj;
}
public void Display()
{
foreach (int i in list)
{
Console.Write(i + ", ");
}
Console.WriteLine(Name);
}
}

执行结果

浅层拷贝

 class Program
{
static void Main(string[] args)
{
ShallowCopy obj1 = new ShallowCopy();
obj1.Name = "Tom";
ShallowCopy obj2 = (ShallowCopy)obj1.Clone();
obj1.Name = "Jerry";
obj1.list[] = ;
obj1.Display();
obj2.Display();
Console.WriteLine(object.ReferenceEquals(obj1, obj2));
Console.ReadKey();
}
}
[Serializable]
public class ShallowCopy : ICloneable
{
public List<int> list = new List<int>() { , , , };
public string Name { get; set; } public Object Clone()
{
object obj = this.MemberwiseClone();
return obj;
}
public void Display()
{
foreach (int i in list)
{
Console.Write(i + ", ");
}
Console.WriteLine(Name);
}
}

执行结果:

8、其他克隆方式

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

8.2  用反射进行克隆

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

   class Program
{
static void Main(string[] args)
{
MyContainer con1 = new MyContainer();
MyContainer con2 = (MyContainer)con1.Clone();
con1.myArray[].ID = ;
Console.WriteLine(con1.myArray[].ID);
Console.WriteLine(con2.myArray[].ID);
Console.WriteLine(con1.myArray[].Name + " " + con2.myArray[].Name);
Console.WriteLine(object.ReferenceEquals(con1, con2));
Console.ReadKey();
}
}
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
}
public class MyContainer : BaseObject
{
public MyClass[] myArray = new MyClass[];
public MyContainer()
{
for (int i = ; i < ; i++)
{
MyClass myClass = new MyClass();
myClass.ID = i;
myClass.Name = "Tom";
this.myArray[i] = myClass;
}
}
} /// <summary>
/// BaseObject类是一个用来继承的抽象类。
/// 每一个由此类继承而来的类将自动支持克隆方法。
/// 该类实现了Icloneable接口,并且每个从该对象继承而来的对象都将同样地
/// 支持Icloneable接口。
/// </summary>
public abstract class BaseObject : ICloneable
{
/// <summary>
/// 克隆对象,并返回一个已克隆对象的引用
/// </summary>
/// <returns>引用新的克隆对象</returns>
public object Clone()
{
//首先我们建立指定类型的一个实例
object newObject = Activator.CreateInstance(this.GetType());
//我们取得新的类型实例的字段数组。
FieldInfo[] fields = newObject.GetType().GetFields();
int i = ;
foreach (FieldInfo fi in this.GetType().GetFields())
{
//我们判断字段是否支持ICloneable接口。
Type ICloneType = fi.FieldType.GetInterface("ICloneable", true);
if (ICloneType != null)
{
//取得对象的Icloneable接口。
ICloneable IClone = (ICloneable)fi.GetValue(this);
//我们使用克隆方法给字段设定新值。
fields[i].SetValue(newObject, IClone.Clone());
}
else
{
// 如果该字段不支持ICloneable接口,直接设置即可。
fields[i].SetValue(newObject, fi.GetValue(this));
}
//现在我们检查该对象是否支持IEnumerable接口,如果支持,
//我们还需要枚举其所有项并检查他们是否支持IList 或 IDictionary 接口。
Type IEnumerableType = fi.FieldType.GetInterface("IEnumerable", true);
if (IEnumerableType != null)
{
//取得该字段的IEnumerable接口
IEnumerable IEnum = (IEnumerable)fi.GetValue(this);
Type IListType = fields[i].FieldType.GetInterface("IList", true);
Type IDicType = fields[i].FieldType.GetInterface("IDictionary", true);
int j = ;
if (IListType != null)
{
//取得IList接口。
IList list = (IList)fields[i].GetValue(newObject);
foreach (object obj in IEnum)
{
//查看当前项是否支持支持ICloneable 接口。
ICloneType = obj.GetType().GetInterface("ICloneable", true);
if (ICloneType != null)
{
//如果支持ICloneable 接口,
//我们用它来设置列表中的对象的克隆
ICloneable clone = (ICloneable)obj;
list[j] = clone.Clone();
}
//注意:如果列表中的项不支持ICloneable接口,那么
//在克隆列表的项将与原列表对应项相同
//(只要该类型是引用类型)
j++;
}
}
else if (IDicType != null)
{
//取得IDictionary 接口
IDictionary dic = (IDictionary)fields[i].GetValue(newObject);
j = ;
foreach (DictionaryEntry de in IEnum)
{
//查看当前项是否支持支持ICloneable 接口。
ICloneType = de.Value.GetType().
GetInterface("ICloneable", true);
if (ICloneType != null)
{
ICloneable clone = (ICloneable)de.Value;
dic[de.Key] = clone.Clone();
}
j++;
}
}
}
i++;
}
return newObject;
}
}

8.3 使用序列化进行克隆

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

8.4 使用IL进行克隆 (代码希望有大牛能实现,本人小白^_^)

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

8.5 使用扩展方法进行克隆 (代码希望有大牛能实现,本人小白^_^)

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

9、总结

.NET中使用Object的MemberwiseClone()方法来实现浅拷贝,通过序列化和反序列化实现深拷贝。

10、参考

http://www.cnblogs.com/jiangj/archive/2010/08/20/1804518.html shaya

http://www.cnblogs.com/nianshi/archive/2008/01/04/1025542.html 念时

http://www.cnblogs.com/caoben313/archive/2010/08/11/1797230.html 林声歌

http://liping13599168.cnblogs.com/  Leepy

http://www.soaspx.com/dotnet/csharp/csharp_20110716_7878.html

http://www.csharpwin.com/csharpspace/9308r573.shtml

http://www.cnblogs.com/luckeryin/archive/2010/03/17/1688106.html

作者:MrZivChu

2013-08-02 23:22:52

net对象的克隆的更多相关文章

  1. java对象 深度克隆(不实现Cloneable接口)和浅度克隆

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt128 为什么需要克隆: 在实际编程过程中,我们常常要遇到这种情况:有一个对象 ...

  2. Java基础学习 —— 对象的克隆

    对象的克隆分为对象的浅克隆和深克隆 一.对象的浅克隆 注意事项: 1.如果一个对象需要调用clone的方法克隆,那么该对象必须要实现Cloneable接口 2.Cloneable对象只是一个标识对象, ...

  3. 【JAVA零基础入门系列】Day14 Java对象的克隆

    今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...

  4. Java对象的克隆和深浅问题

    Java实现克隆的方式 Java实现克隆的方式有如下两种, 推荐采用实现Cloneable接口的方式 实现Cloneable接口, 重写clone方法, 调用父类的clone方法 还有另一种方法, 不 ...

  5. java基础知识总结--对象的克隆

    前提:在Java语言中所有的类的都是缺省的继承Java语言中的Object类的, protected native Object clone() throws CloneNotSupportedExc ...

  6. Java基础--对象的克隆

    文章转载自https://www.cnblogs.com/Qian123/p/5710533.html 阅读目录 为什么要克隆? 如何实现克隆 浅克隆和深克隆 解决多层克隆问题 总结 假如说你想复制一 ...

  7. Java对象的克隆

    今天要介绍一个概念,对象的克隆.本篇有一定难度,请先做好心理准备.看不懂的话可以多看两遍,还是不懂的话,可以在下方留言,我会看情况进行修改和补充. 克隆,自然就是将对象重新复制一份,那为什么要用克隆呢 ...

  8. 设计模式系列之原型模式(Prototype Pattern)——对象的克隆

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  9. JAVA笔记10__Math类、Random类、Arrays类/日期操作类/对象比较器/对象的克隆/二叉树

    /** * Math类.Random类.Arrays类:具体查JAVA手册...... */ public class Main { public static void main(String[] ...

随机推荐

  1. nutzwk运行后wk-web中生成ehcache.disk.store.dir有什么用,怎么去掉

    nutzwk运行后wk-web中生成ehcache.disk.store.dir有什么用,怎么去掉  发布于 29天前  作者 qq_96c46988  64 次浏览  复制  上一个帖子  下一个帖 ...

  2. 类型构造器--高阶类型(构造器):Kind (type theory)--类型的元

    元类型(0阶类型):nullary type, data types 一元类型(一阶类型):unary  adj. [数] 一元的   二元类型: is the kind of a binary ty ...

  3. GPGPU::数学基础教程

    并行方向需要学习的数学知识 http://dev.gameres.com/Program/Visual/3D/GPGPU_math_Tutorial.html

  4. Android 最新学习资料收集

    收集这份资料的灵感来源于我的浏览器收藏夹快爆了,后来在github 上也看到了很优秀的开源库的收集资料,非常的好,但是太过于多,也不够新,所以决定自己来做一个.原始的markdowm文件已经放到git ...

  5. 知识总结和记录——HTML

    文档结构 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="U ...

  6. swiper轮播始终居中active图片

    用的是vue-awesome-swiper 在vue项目中,参数方法与swiper一致.使用场景如下: 左侧小图一共八张,默认显示的是三张,始终保持activeimg在中间,提升用户体验度.swipe ...

  7. 微信 php 获取token 第二次失败解决办法

    第一次成功了,第二次总是失败,很简单,session问题 clearstatcache(); $_SESSION = ''; $_COOKIE = ''; //获得参数 signature nonce ...

  8. Linux基础知识随笔记

    linux文件属性 ls -h human-readable以人类可读的形式显示 -i 显示inode号码 [root@oldboyedu55-bjb ~]# ls -ihl total 8.0K 3 ...

  9. 使用 python快速搭建http服务

    在 Linux 服务器上或安装了 Python 的机器上,Python自带了一个WEB服务器 SimpleHTTPServer. 我们可以很简单的使用  python -m SimpleHTTPSer ...

  10. React路由-基础篇

    React-Router-DOM ReactRouter网址, 安装 -npmjs找到react-router-dom -yarn add react-router-dom 基本使用方法 1.创建一个 ...