我们都知道,C#的变量在类型上可以分为值类型和引用类型。
所谓值类型就是这个变量存储的是一个具体的值,两个值类型的值是相互独立的,修改一个不会影响另一个。例如:
int i = 1;int j = i;
i++;
Console.WriteLine(i + " " + j); // 输出 2 1
引用类型则类似C++的指针,变量里面存放的是具体的内存地址,修改一个会影响另一个。例如:
public class EXPoint3d
{
public int x;
public int y;
public EXPoint3d(int x, int y)
{
this.x = x;this.y = y;
}
public EXPoint3d(EXPoint3d point)
{
this.x = point.x; this.y = point.y;
}
}
EXPoint3d str1 = new EXPoint3d(11,22), str2 = str1;
str1.x++;
Console.WriteLine(str2.x + " " +str2.y);
List是C#中一个很好用的高效的存储容器,我们也经常会碰到要将一个list中的内容复制到另一个的需求。
这时候就要注意了,如果List中存放的是引用类型的数据,例如我们自己定义的class,那么如果直接使用常规赋值方法复制的话。复制出来的list实际上是原list的一个拷贝,例如:
List<EXPoint3d> list1 = new List<EXPoint3d>(); // 原始list
list1.Add(new EXPoint3d(1, 1));
list1.Add(new EXPoint3d(2, 2));
list1.Add(new EXPoint3d(3, 3));
List<EXPoint3d> list2 = new List<EXPoint3d>();
foreach (EXPoint3d point in list1) // 第一种复制方法
list2.Add(point);
List<EXPoint3d> list3 = new List<EXPoint3d>(list1); // 第二种复制方法
List<EXPoint3d> list4 = list1.ToList(); // 第三种复制方法
list1[0].x += 2;
Console.WriteLine(list1[0].x + " " + list1[0].y); // 输出 3 1
Console.WriteLine(list2[0].x + " " + list2[0].y); // 输出 3 1
Console.WriteLine(list3[0].x + " " + list3[0].y); // 输出 3 1
Console.WriteLine(list4[0].x + " " + list4[0].y); // 输出 3 1
上面的三种常规复制方法,所复制的都只是原有list的浅拷贝也就是引用,修改源list一定会导致复制的list也会发生相应变动。
那么如何进行深拷贝也就是使得复制出来的list不受源list影响呢?这里有几种方法。
最常规的方法就是使用for循环在每次赋值的时候new 一下,这样当然行,不过这里介绍另外的方法:
List<EXPoint3d> list1 = new List<EXPoint3d>(); // 原始list
list1.Add(new EXPoint3d(1, 1));
list1.Add(new EXPoint3d(2, 2));
list1.Add(new EXPoint3d(3, 3));
List<EXPoint3d> list5 = new List<EXPoint3d>();
foreach (EXPoint3d point in list1) // 第一种复制方法
list5.Add(new EXPoint3d(point.x, point.y));
List<EXPoint3d> list6 = list1.Select(t => new EXPoint3d(t.x, t.y)).ToList(); // 第二种方法
list1[0].x += 2;
Console.WriteLine(list5[0].x + " " + list5[0].y); // 输出 1 1
Console.WriteLine(list6[0].x + " " + list6[0].y); // 输出 1 1
有的时候我们还需要按照一定的筛选条件将某个list中的内容复制到另一个list。这里可以使用for循环进行逐个判断,也有一种简便写法。
这个简便写法的语法与SQL结构化查询语言中的select where条件判断是否的类似,举例:
List<EXPoint3d> list1 = new List<EXPoint3d>(); // 原始list
list1.Add(new EXPoint3d(1, 1));
list1.Add(new EXPoint3d(2, 2));
list1.Add(new EXPoint3d(3, 3));
List<EXPoint3d> list7 = (from data in list1 where data.x >= 1 && data.y <= 1 select new EXPoint3d(data.x, data.y)).ToList();
List<EXPoint3d> list8 = (from data in list1 where data.x >= 1 && data.y <= 1 select data).ToList();
list1[0].x += 2;
Console.WriteLine(list7[0].x + " " + list7[0].y); // 输出 1 1
Console.WriteLine(list8[0].x + " " + list8[0].y); // 输出 3 1
上面的list7和list8都是按照语法条件【x大于等于一,并且y小于等于一】筛选出来的list1中的对象,区别在于list7是使用了new而list8只是源list1的引用拷贝。
所以修改list1的值会影响到list8但不会对list7造成影响。