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程序)
小结,在写长代码片段是,对临时变量的管理特重要,在这个例子中,因为要对新对象处理,对旧对象使用检索,所以不能让新对象影响旧对象,看上去自然的逻辑,其实上错在仅仅的一行代码,这是很冤枉的。best regards希望能腾点时间来写wpf的专题。补充1: 很不幸,真的只能说是技术没到家:。 对于List这样的集合,提供了支持深复制的方法,GetRange,还是直接上代码:
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是使用一个值的,这就是引用类型的特点。最后说明注意场景,例子仅自己今天遇到的,还是直接上代码(有点长):
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
相关文章推荐
- 使用C#在进度条中显示复制文件的进度
- 使用C#在进度条中显示复制文件的进度
- 使用C#在进度条中显示复制文件的进度
- 使用C#在进度条中显示复制文件的进度
- 病毒及流氓软件自我复制的简单实现(C#)
- 使用C#和Excel进行报表开发(四)-复制单元格
- 使用C#在进度条中显示复制文件的进度
- 使用C#在进度条中显示复制文件的进度
- C#:复制对象的事例
- 使用C#在进度条中显示复制文件的进度
- 文件夹复制删除 C#
- C# 打开文本文件,剪切文本,复制文本,粘贴文本方法
- 病毒及流氓软件自我复制的简单实现(C#)
- 使用C#和Excel进行报表开发(四)-复制单元格
- 使用C#在进度条中显示复制文件的进度
- C#递归复制文件
- [懵懂边缘]使用C#在进度条中显示复制文件的进度
- C#中屏蔽复制粘贴
- C#:浅谈对象数组,运算符重载和深度复制的应用
- C#中将原表复制到新表