我们都知道,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造成影响。