拷贝(copy):
什么是拷贝:通常意义来说也就是复制,对象的拷贝也就是将对象复制出来一个一样的新对象出来。虽然都是复制对象,但是不同的复制方法,复制出来的对象确不是一样的,存在一些差异。
有哪些拷贝方法:分为浅拷贝与深拷贝
浅拷贝(shallow copy)
什么是浅拷贝:字面意思 赋值浅浅的一层
将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指 复制它的一个引用到目标对象。如果改变目标对象中引用型字段的值他将反 映在原始对象中,也就是说原始对象中对应的字段也会发生变化。
- 对于值类型对象,会按位复制出一个全新的对象。
-
int a=1; int b=a;
- 对于含有引用类型对象的值类型对象,值类型对象会按位复制引用类型对象的变量,但是新的引用类型变量会指向与之前老的引用类型对象变量所指向的相同对象。在下面的例子中,Name和Alias都是引用类型对象,所以它们在浅拷贝之后指向的是原来的Name和Alias指向的对象。
-
/// <summary> /// 值类型 /// </summary> public struct ValExample { //引用类型 public string Name { get; set; } public int ValCount { get; set; } public List<string> Alias { get;set;} } ValExample var = new ValExample() { Alias = new List<string>(), Name = "Hello" }; ValExample var1 = var;
- 对于引用类型对象,会按位复制出一个全新的引用变量,但是这个引用变量会指向当前引用类型对象。注意,对于引用类型对象,不能简单使用=实现浅拷贝,需要使用Object自带的MemberwiseClone。
-
.net提供了一个ICloneable接口,该接口下有一个Clone()方法,你可以实现它用来实现你自己的克隆方式,比如深克隆或是浅克隆,MemberwiseClone()是object类中的一个方法,用来实现类的浅克隆
/// <summary>
/// 引用类型
/// </summary>
public class RefExample
{
public int RefCount { get; set; }
}
RefExample ref1=new RefExample();//在堆上分配
ref1.RefCount = 1;
RefExample ref2=ref1.clone();
深拷贝(deep copy)
什么是浅拷贝:字面意思 完全复制一个对象
不论是引用类型对象还是值类型对象,对其中包含的引用对象也会创造一个 全新的对象,新产生的对象和老对象没有关系,不会担心会相互影响
通过序列化实现深拷贝
Serializable这个属性大家都不陌生,标记为这个属性的类可以被序列化,通过序列化和反序列化,我们可以实现深拷贝。
[Serializable]
class UsingSerilizable
{
public string Name { get; set; }
public List<string> Alias { get; set; }
}
UsingSerilizable u1 = new UsingSerilizable() {Name = "Deatharthas", Alias = new List<string>()};
using (var ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, u1);
ms.Seek(0, SeekOrigin.Begin);
var u2 = (UsingSerilizable)bf.Deserialize(ms);
}
这样就实现了深拷贝,但是这种方法有一个缺点,如果对象包含的属性中,包含有不可序列化的类型,那么这个调用就会抛异常。我们试着修改上面的UsingSerilizable类。
使用Json实现的深拷贝
首先,我们引入大名鼎鼎的Newton.Json.dll。
接着,搞出一个实现Deep Copy的泛型扩展方法,以方便不同类型使用。
public static class ExtentionForDeepCopy
{
public static T DeepCopy<T>(this T original)
{
return (T)JsonConvert.DeserializeObject(JsonConvert.SerializeObject(original), original.GetType());
}
}
UsingSerilizable u1 = new UsingSerilizable() {Name = "h", Alias = new List<string>() {"a", "b"}, EmptyProperty= new Empty()};
var u2 = u1.DeepCopy();
区别:
- 对引用的处理,深拷贝将会在新对象中创建一个新的和原始对象中对应字段相同的字段,也就是这个引用和原始对象的引用是不同
- 值类型通过=实现浅拷贝
- 引用类型通过MemberwiseClone实现浅拷贝
- 没有内置的支持深拷贝的函数,但是我们可以借助Newton.Json.dll来实现
- 不管是深拷贝还是浅拷贝,都不会复制全局数据区的成员,因为全局数据区的成员是静态成员,它属于某一个类,并不属于类的实例对象,因此无法复制。