您的位置:首页 > 编程语言 > C#

C# 浅复制和深复制

2017-03-27 23:50 295 查看
Hi all,           其实最近一直在做WPF的项目,关于UI,MVVM,多线程,devexpress都想写些东西,可惜忙着,虽然平时感悟挺深,也来不及整理,也不想随便写写忽悠大家,毕竟不是那种随便写写都能写出好文的大神,只是想好好的分享下项目过程中遇到的一些容易放错的地方,俗话说“知错能改”嘛。           嘻嘻,其实这篇也是忽悠,只是今天在写复杂function时遇到而已,一时没注意就错了都不知道。           以下仅个人理解加某些专业术语,有不对的地方请原谅。           “浅复制是对引用类型地址的复制,而深复制是对值类型的复制。”             这句话有两个个名词,             值类型:派生自System.ValueType的类型,我们常见的int  char  long  struct  short 等等的数值或结构体或enum,             引用类型:派生自System.object的类型,如string   数组array  类class  list 等高级的数据类型。         文字不好说,还是直接上代码说明:(一个极简的CLI程序)       
            string aaa = "have";string bbb = aaa;bbb = bbb + "_lin";Console.WriteLine(aaa);
           结果是:have。           这个例子有点特殊,故意举例这个的,虽说string是引用类型,但是在等号复制的过程中还是“深复制”,具体原因查过资料才知道:             
//表示空字符串。此字段为只读。
      public static readonly string Empty;
         答案就在于 string 是 readonly 的,当改变 string 类型的数据值时,将重新分配了内存地址。            我们举另一个例子List类型:               List<string> arr1 = new List<string>();arr1.Add("1");arr1.Add("2");arr1.Add("3");var arr2 = arr1;arr2[1] = "lin";foreach(string s in arr1){Console.WriteLine(s);}              结果是:“1”   “lin”  "3"  ,            上面的程序中,我们修改的是arr2,但arr1也同时被修改了,这说明arr1和arr2是使用一个值的,这就是引用类型的特点。最后说明注意场景,例子仅自己今天遇到的,还是直接上代码(有点长):     小结,在写长代码片段是,对临时变量的管理特重要,在这个例子中,因为要对新对象处理,对旧对象使用检索,所以不能让新对象影响旧对象,看上去自然的逻辑,其实上错在仅仅的一行代码,这是很冤枉的。best regards希望能腾点时间来写wpf的专题。补充1:    很不幸,真的只能说是技术没到家:。    对于List这样的集合,提供了支持深复制的方法,GetRange,还是直接上代码:
 List<string> arr1 = new List<string>();arr1.Add("1");arr1.Add("2");arr1.Add("3");var arr2 = arr1;changeList(arr2.GetRange(0, arr2.Count));arr2[1] = "lin";foreach (string s in arr1){Console.WriteLine(s);}private static void changeList(List<string> list)        {            if (list.Count != 0)            {                list[0] = "haha";            }        }
最后结果是:‘1’    ‘lin’    ‘3’代码里头有两处地方修改List,分别是index 0和1,但最终只有index 1改变,为此,说明GetRange方法是对arr2对象的一个深复制。回过头来写这一段,也是为了指出之前犯的错误(CTO跟我说你那个函数迟早会有问题),我才痛定思痛的看了不知道多少遍,直接上代码说明:Model层数据
//使用localDB连接数据库获取数据public static Player[] GetPlayerByRegionId(Guid regionId){using (var dbContext = new LocalModel()){var players = (from p in dbContext.Players where p.RegionId == regionId select p).ToArray();return players;}}
Control层调用
        private void Sample1(Guid regionId){var players1 = LocalDBAccess.GetPlayerByRegionId(regionId);List<Player> FirstListObj = new List<Player>(players1);//接下来是复制对象部分,先举错误示例,//用我上面的方法获取一个相同的对象,做法是这样var playeres2 = LocalDBAccess.GetPlayerByRegionId(regionId);List<Player> LaterListObj = new List<Player>(playeres2);//然后各自操作  FirstListObj  ,,LaterListObj..............}
这里想法上是没有错误的,只是在大型一点的工程,流量大的网站,我们就不能保证 FirstListObj 和 LaterListObj 是一样的。在没有访问锁(访问限制)的数据库中,在user1执行完 GetPlayerByRegionId 方法后,来自user2 给 相关表添加一条数据,那么用户再次执行 GetPlayerByRegionId 方法时,他获取到的 LaterListObj 就与FirstListObj 不一致,并没有实现复制的功能。下面改变Control层调用
private void Sample2(Guid regionId){var players1 = LocalDBAccess.GetPlayerByRegionId(regionId);List<Player> FirstListObj = new List<Player>(players1);//接下来是复制对象部分,正确示例,//使用GetRange方法,其他的集合对象也有类似的方法var LaterListObj = FirstListObj.GetRange(0, FirstListObj.Count);//然后各自操作  FirstListObj  ,,LaterListObj............}
这里我们对数据连接只有一次,所以数据库的改变并不会马上反应到变量中,所有操作均是在基础数据上。最后的最后,特意挖出来重新举例,一是承认自己的错,而勉励自己继续努力。最后修改2017年3月31日11:15:39 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息