有的时候我们可能在实际的项目中需要一个对象在某个状态下的副本,这个前提很重要,这点怎么理解呢,例如有的时候我们需要对比一个对象经过处理后的状态和处理前的状态是否发生过改变,可能我们就需要在执行某段处理之前,克隆这个对象此时状态的副本,然后等执行后的状态进行相应的对比,这样的应用在项目中也是经常会出现的。
例如:我们有一个对象 A a=new A(); 在进行实际操作完这个A对象之后,它的属性发送了一些变化;我们要与于在操作完它之前的状态进行比较,这个时候我们使用复制的方式保存原来的状态,避免了重新新建一个对象一个个进行属性赋值的方式进行保存。
一. 浅度复制:
系统为我们内置提供了复制对象本身的方法,不过这个方法返回的是一个浅复制的对象副本,而且.NET给我提供了一个System.ICloneable的接口,我们通过实现这个接口,可以为对象提供自定义的克隆方法。为了搞明白浅复制和深复制,那么我先要搞懂这2者的区别。先看下面的代码:
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Text;
-
-
namespace
ConsoleApplication4
-
{
-
/// <summary>
-
/// 杯子的局部颜色属性
-
/// </summary>
-
public
class
Colors
-
{
-
public
string Top {
get;
set; }
-
public
string foot {
get;
set; }
-
}
-
}
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Text;
-
-
namespace
ConsoleApplication4
-
{
-
/// <summary>
-
/// 杯子的其他特征,继承ICloneable
-
/// </summary>
-
public
class
Cup:
ICloneable
-
{
-
public
int Height {
get;
set; }
-
public
int RL {
get;
set; }
-
public Colors c {
get;
set; }
-
-
/// <summary>
-
/// ICloneable 成员
-
/// </summary>
-
/// <returns></returns>
-
public object Clone()
-
{
-
return
this.MemberwiseClone();
-
}
-
}
-
}
-
using System;
-
using System.Collections.Generic;
-
using System.Linq;
-
using System.Text;
-
-
namespace
ConsoleApplication4
-
{
-
/// <summary>
-
/// 深度复制和浅度复制
-
/// </summary>
-
class
Program
-
{
-
static void Main(string[] args)
-
{
-
Cup cup =
new Cup();
-
cup.Height =
20;
-
cup.RL =
200;
-
cup.c =
new Colors() { foot =
"白色", Top =
"无色" };
-
-
Console.WriteLine(
"cup-Height:" + cup.Height);
//20
-
Console.WriteLine(
"cup-RL:" + cup.RL);
//200
-
Console.WriteLine(
"cup-foot:" + cup.c.foot);
//白色
-
-
Cup cup1 = (Cup)cup.Clone();
-
cup1.RL =
100;
-
cup1.Height =
50;
-
cup1.c.foot =
"灰色";
-
Console.WriteLine(
"cup1-Height:" + cup1.Height);
//50
-
Console.WriteLine(
"cup1-RL:" + cup1.RL);
//100
-
Console.WriteLine(
"cup-foot:" + cup1.c.foot);
-
-
Console.WriteLine(
"cup-Height:" + cup.Height);
//20
-
Console.WriteLine(
"cup-RL:" + cup.RL);
//200
-
Console.WriteLine(
"cup-foot:" + cup.c.foot);
//灰色 值发生改变了
-
}
-
}
-
}
上面的代码的运行情况如下:
分析上面的代码的运行情况:
我们得出以下的结论对于值类型的成员,浅复制也是在副本中重新创建的成员,对应到内存的栈上,分配新的内存空间。那么对于引用类型则因为浅复制的时候,对象和对象副本共用同一个引用对象,那么不管是在对象还是对象副本中修改了相应的引用成员了之后,那么这个引用类型的成员就会发生变化。此处最好的例子就是cup1对象进行浅度复制后修改了颜色导致cup对象的颜色发生了变化。因为2个对象指向同一个内存地址,那么任何一个修改操作都会产生改变。使用深度复制就可以解决这一问题。
二 . 深度复制:
我们修改上诉代码中的Cup Clone()方法:
-
/// <summary>
-
/// ICloneable 成员
-
/// </summary>
-
/// <returns></returns>
-
public object Clone()
-
{
-
Cup cup = (Cup)
this.MemberwiseClone();
-
Colors c =
new Colors();
-
c.Top =
this.c.Top;
-
c.foot =
this.c.foot;
-
cup.c = c;
-
return cup;
-
}
不管是值类型的成员还是引用类型的成员,这样的对象和对象副本,对任何一个成员属性的修改,都不会影响到改变对象的值。使用这样的方式完成对象的深度复制。