也许会有人这样解释C# 中浅拷贝与深拷贝区别
浅拷贝是对引用类型拷贝地址对值类型直接进行拷贝
不能说它完全错误但至少还不够严谨比如string 类型咋说?
其实我们可以通过实践来寻找答案
首先定义以下类型
int string enum struct class int[ ] string[ ]
代码如下
//枚举
public enum myEnum
{ _ = _ = }
//结构体
public struct myStruct
{
public int _int;
public myStruct(int i)
{ _int = i; }
}
//类
class myClass
{
public string _string;
public myClass(string s)
{ _string = s; }
}
//ICloneable创建作为当前实例副本的新对象
class DemoClass : ICloneable
{
public int _int = ;
public string _string = ;
public myEnum _enum = myEnum_;
public myStruct _struct = new myStruct();
public myClass _class = new myClass();
//数组
public int[] arrInt = new int[] { };
public string[] arrString = new string[] { };
//返回此实例副本的新对象
public object Clone()
{
//MemberwiseClone返回当前对象的浅表副本(它是Object对象的基方法)
return thisMemberwiseClone();
}
}
注意
ICloneable 接口支持克隆即用与现有实例相同的值创建类的新实例
MemberwiseClone 方法创建当前 SystemObject 的浅表副本
接下来构建实例A 并对实例A 克隆产生一个实例B
然后改变实例B 的值并观察实例A 的值会不会被改变
代码如下
class 浅拷贝与深拷贝
{
static void Main(string[] args)
{
DemoClass A = new DemoClass();
//创建实例A的副本 > 新对象实例B
DemoClass B = (DemoClass)AClone();
B_int = ;
ConsoleWriteLine( int \t\t A:{} B:{} A_int B_int);
B_string = ;
ConsoleWriteLine( string \t A:{} B:{} A_string B_string);
B_enum = myEnum_;
ConsoleWriteLine( enum \t\t A:{} B:{} (int)A_enum (int)B_enum);
B_struct_int = ;
ConsoleWriteLine( struct \t A:{} B:{}
A_struct_int B_struct_int);
B_class_string = ;
ConsoleWriteLine( class \t\t A:{} B:{}
A_class_string B_class_string);
BarrInt[] = ;
ConsoleWriteLine( intArray \t A:{} B:{}
AarrInt[] BarrInt[]);
BarrString[] = ;
ConsoleWriteLine( stringArray \t A:{} B:{}
AarrString[] BarrString[]);
ConsoleReadKey();
}
}
结果如下
从最后的输出结果我们得知
对于内部的Class 对象和数组则Copy 一份地址[ 改变B 时A也被改变了 ]
而对于其它内置的int / string / enum / struct / object 类型则Copy 一份值
有一位网友说string 类型虽然是引用类型但是很多情况下Net 把string 做值类型来处理我觉得string 应该也是按照值类型处理的
这说明他对string 类型还不够了解
可以肯定的是string 一定是引用类型那它为什么是深拷贝呢?
如果你看一下string 类型的源代码就知道了
//表示空字符串此字段为只读
public static readonly string Empty;
答案就在于 string 是 readonly 的当改变 string 类型的数据值时将重新分配了内存地址
下面引用一段网友的代码Vseen[ Aloner ] 的个人陋见
public class Student
{
// 这里用字段其实应当是属性
public string Name;
public int Age;
//自定义类 Classroom
public Classroom Class;
}
浅拷贝Student A 浅拷贝出 Student BName和Age拥有新的内存地址但引用了同一个 Classroom
深拷贝Student A 浅拷贝出 Student BName和Age拥有新的内存地址并且AClassroom 的内存地址不等于 BClassroom
其实俗点讲有点像
public object Clone()
{
Student B = new Student();
BName = thisName;
BAge = thisAge;
//浅拷贝
BClass = thisClass;
//深拷贝
BClass = new Classromm();
BClassName = thisClassName;
BClassTeacher = thisClassTeacher;
//根据情况对Teacher 进行判定要进行的是深拷贝还是浅拷贝
}
浅拷贝给对象拷贝一份新的对象
浅拷贝的定义 —— 只对值类型(或string)类型分配新的内存地址
深拷贝给对象拷贝一份全新的对象
深拷贝的定义 —— 对值类型分配新的内存地址引用类型以及引用类型的内部字段分配的新的地址
我是这么定义的浅拷贝换汤不换药
注意
在 NET 程序中应该避免使用 ICloneable 接口
因为通过该接口无法判断究竟是浅拷贝还是深拷贝这会造成误解或误用
深拷贝应该复制该对象本身及通过该对象所能到达的完整的对象图浅拷贝只复制对象本身(就是该对象所表示的在堆中的一块连续地址中的内容)
个人愚见
Clone 深层拷贝拷贝到了指针指向的内存块的值
浅拷贝仅仅拷贝了指针的内容(只是给一个对象多起了个名字所以当改变拷贝的某个属性的时候原对象的对应属性亦会改变)