C#数据类型主要分为两种,值类型和引用类型,
值类型主要包括:数值型 bool型 结构体 枚举 可空类型
引用类型主要包括:字符串 集合 类的实体(接口) 数组 委托
其中,值类型都是存储在栈上的,但是栈的容量相对于堆来讲,就比较小,而引用类型是在栈从一个引用,指向堆,就可以获得访问堆的一部分数据结构的权利。
假如我们int[] A =new int[4];
其实,在内存中开辟了实例如下的空间
而值类型就是直接开辟在栈里面,不过,有点儿特殊的是结构体
它引用的依然是栈上的内存区域。
那么,在我们了解了什么是值类型和引用类型之后,就聊什么是深复制和浅复制。
int[] a = new int[5] { 1, 2, 3, 4, 5 };
int[] temp = a;
temp[2] = 400;
foreach (var item in a) {
Console.WriteLine(item);
}
其实,在这段代码中,就是我们常说的浅复制,
就是Temp和a同时引用了同一块内存区域。
其内存中的结构大致如下
往往这样是达不到我们的要求的,而且有时候还会有意想不到的错误。
所以,我们要进行深复制,其中,我们要实现一个比较重要的接口是ICloneable
下面是我们编写的一个测试类,
public class Employee : ICloneable
{
public string Name;
public string Title;
public int Age;
// Simple Emplyee constructor
public Employee(string name, string title, int age)
{
Name = name;
Title = title;
Age = age;
}
public object Clone()
{
return MemberwiseClone();
}
}
其中,MemberwiseClone就是底层专门为我们提供的对象深复制的方法,在调用中,可以这样写:
Employee e = new Employee("yangxu","测试",21);
Employee newE = e.Clone() as Employee;
其中 as的语法是此对象是否能够转型成为某个对象,如果能,返回其引用,如果不能,返回null。
如果您使用过Entity Framework,经常会遇到序列化器检测到循环递归调用的错误,那么,在序列化EF代理类对象之前,我们将其转化为一般的对象,就可以通过使用深复制实现,从而避免稀奇古怪的错误。
在了解了对象的深复制和浅复制的差别之后,我们自然可以聊到ref和out关键字,其使用场景就是,当你的返回类型特别多的时候,而函数返回的个数达不到需要,那么,我们就可以通过out和ref关键字,通过外界先定义变量,进行相应的计算以后,然后原来定义的外界变量得到了修改。
out和ref没有什么区别,只是在使用的时候,ref必须要初始化,而out不一定,并且,out和ref不会构成重载。