【C# .Net】List循环add,出现数据相同现象? 引发对引用类型和值类型的底层逻辑的思考。
赶项目时发现了一个问题,定义一个引用对象,如果在循环外定义对象,在循环内list.add(object)。最后的结果却是所有的对象值都是一样的,即每add一次,都会把之前的数据覆盖。
解决方法:把对象在循环里new就行了,这样并不会造成很大的内存消耗,因为循环结束new的对象很快都被GC了。
虽然解决了此问题,但想感觉里面的逻辑有点意思,想深入了解里面包含了一些.net底层存储的知识,引用类型和值类型的区别,还有String这个特殊的引用类型的探究。
首先说一下结论:对于List<T>来说,如果T是引用类型,那保存的是引用,如果是值类型,保存的是值本身。
下面是demo,分别是 object,int ,string 。

结果分别是:

如果不在循环内部创建对象, 一般情况下,引用类型(除了string)会被覆盖,而值类型不会被覆盖,这是什么原因呢?
分析:
请观察下图,new User1对象,然后用User2 = User1 给User2 赋值。

值类型(int,stuct,bool,enum,float,decimal),声明后,无论是否有值,编译器先分配其内存(分配在栈)。
引用类型(object,interface,delegate,array)引用类型当声明一个类时,只在栈中分配内存用于容纳地址,而此时并没有为其分配堆上的内存空间。当new 一个实例时,分配至堆上,并把堆的地址保存到栈上。
回到上面的例子,对于引用类型,在循环外new了 user 对象后,这个对象的引用地址就确定了。到第二次ladd时,list[0]中保存的User对象和list[1]对象是同一个对象,使用的是同一个地址,也就是说在添加list[1]是,list[0]也被修改了,因为它俩指针指向同一个地址,结果就是list都是最后的list[i]的数据。
其他:String是一种特殊的引用类型
String类型直接继承自Object,这使得它成为一个引用类型,也就是说栈上不会有任何字符串。但是与其他引用类型不同是,string具有不变性。
String的不变性:
String的值改变时,会检查内存,如果与原来的值不同,则会重新分配内存空间,分配地址到栈上,数据到堆上,而不会影响到原有的值。
这个原因也是为什么字符串大范围修改要用StringBuilder,而不是string,每次改变string时,会消耗内存,频繁的处理string对象,会消耗大量的内存。
发散一下思考:
出现以下情况是因为 == 如果比较的是引用类型,那么比较的是引用地址指向的数据是否是同一个,而不是底层对象的实际值。
public class A
{
public string Name;
public A(string n) { Name = n; }
}
A a1 = new A("sima");
A a2 = new A("sima");
Console.WriteLine(a1 == a2); // False
A a3 = a1;
Console.WriteLine(a1 == a3); // true
第二种问题,无法交换两个string。
出现以下原因是因为,参数传递是默认是值传递,Swap方法中的a,b是新在栈中开辟的内存数据,并非参数本身。
解决方法也很简单:使用ref关键字传参改为引用传递。
P.S string是特殊的引用类型,值存储在栈上。

再进一步思考,比如 List 这种东西就有一个奇怪的事情。如果在传参的时候直接把List变为空,竟然无法修改值,引用类型为什么无法修改原有的值
但是如果我对List进行Add 或者Remove 或者赋值的时候原来的值还是会改变???

思考了许久,看了下上文描述引用类型标红语句 引用类型当声明一个类时,只在栈中分配内存用于容纳地址,而此时并没有为其分配堆上的内存空间
再参考了栈堆分配的图,明白了为什么会这样。
因为 List (a1) 在主方法是 值传递过去 创建副本List(a2) 这个栈中属于不同的两个内存,但是他们储存的引用地址是一样的,指向的是堆中同一个内存。
所以副本 List(a2) =null 不影响 List(a1) ,但是对引用到的堆中的数据的修改,会使得指向同一个堆的两个不同 List(a1) List(a2) 结果相同。

【C# .Net】List循环add,出现数据相同现象? 引发对引用类型和值类型的底层逻辑的思考。的更多相关文章
- phalcon: 当删除循环删除一组数据,需要判断影响的行affectedRows
phalcon:有一个表,按日期查找半年以为的数据,由于数据量特别大,不能一次:delete删除数据,否则会造成数据表卡顿,数据库锁死. 那么只能循环的删除数据,每次删除100条左右,知道删除为止., ...
- Oracle游标-循环查询表中数据(表名),并执行
Oralce 表中存有一个字段,该字段存储表名,要把该表中的所有表名查询出来(即表名结果集),且执行结果集from 表名结果集: declare v_ccount ); --定义一个游标变量 curs ...
- JS 循环遍历JSON数据 分类: JS技术 JS JQuery 2010-12-01 13:56 43646人阅读 评论(5) 收藏 举报 jsonc JSON数据如:{"options":"[{
JS 循环遍历JSON数据 分类: JS技术 JS JQuery2010-12-01 13:56 43646人阅读 评论(5) 收藏 举报 jsonc JSON数据如:{"options&q ...
- SQL SERVER 游标循环读取表数据
[cursor]游标:用于循环表行数据,类似指针 格式如下: declare tempIndex cursor for (select * from table) --定义游标 open tempIn ...
- 高效遍历匹配Json数据与双层for循环遍历Json数据
工作中往往遇到这种情况,保留用户操作痕迹,比如用户选择过得东西,用户进入其它页面再返回来用户选择的的数据还在. 比如:1.购物车列表中勾选某些,点击任意一项,前往详情页,再返回购物车依旧需要呈现勾选状 ...
- Ajax请求php返回json对象数据中包含有数字索引和字符串索引,在for in循环中取出数据的顺序问题
//php中的数组数据格式 Array ( [all] => Array ( [title] => ALL [room_promotion_id] => all ) [best_av ...
- js循环读取json数据,将读取到的数据用js写成表格
①js循环读取json数据的方式: var data=[{"uid":"2688","uname":"*江苏省南菁高级中学 022 ...
- Vue之循环遍历Json数据,填充Table表格
简单记一次Vue循环遍历Json数据,然后填充到Table表格中,展示到前端的代码: async getData(id) { const res = await this.$store.api.new ...
- 使用pymysql循环删除重复数据,并修改自增字段偏移值
创建表: CREATE TABLE `info` ( `id` tinyint NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMAR ...
- 生成二维码 加密解密类 TABLE转换成实体、TABLE转换成实体集合(可转换成对象和值类型) COOKIE帮助类 数据类型转换 截取字符串 根据IP获取地点 生成随机字符 UNIX时间转换为DATETIME\DATETIME转换为UNIXTIME 是否包含中文 生成秘钥方式之一 计算某一年 某一周 的起始时间和结束时间
生成二维码 /// <summary>/// 生成二维码/// </summary>public static class QRcodeUtils{private static ...
随机推荐
- [转帖]认识目标文件的格式——a.out COFF PE ELF
https://cloud.tencent.com/developer/article/1446849 1.目标文件的常用格式 目标文件是源代码编译后未进行链接的中间文件(Windows的.obj ...
- Go中字符串处理:fmt.Sprintf与string.Builder的比较
在Go语言中,我们通常会遇到两种主要的方式来处理和操作字符串:使用fmt.Sprintf函数和string.Builder类型.尽管两者都可以实现字符串的格式化和连接,但它们在性能和用法上有一些关键区 ...
- CreateProcess函数源码分析
CreateProcess函数源码分析 源码版本:Windows 2003 源码 源码阅读工具:Source Insight 函数功能分析 函数原型 BOOL CreateProcessA( ...
- 给你一颗“定心丸”——记一次由线上事故引发的Log4j2日志异步打印优化分析
一.内容提要 自知是人外有人,天外有天,相信对于Log4j2的异步日志打印早有老师或者同学已是熟稔于心,优化配置更是信手拈来,为了防止我在这里啰里八嗦的班门弄斧,我先将谜底在此公布:log4j2.as ...
- 小样本学习在文心ERNIE3.0多分类任务应用--提示学习
小样本学习在文心ERNIE3.0多分类任务应用(提示学习) 项目链接: https://aistudio.baidu.com/aistudio/projectdetail/4438610?contri ...
- 强化学习从基础到进阶-案例与实践[4.2]:深度Q网络DQN-Cart pole游戏展示
强化学习从基础到进阶-案例与实践[4.2]:深度Q网络DQN-Cart pole游戏展示 强化学习(Reinforcement learning,简称RL)是机器学习中的一个领域,区别与监督学习和无监 ...
- parser.add_argument()用法——命令行选项、参数和子命令解析器
argparse是一个Python模块:命令行选项.参数和子命令解析器.通过使用这种方法,可以在使用 1.argparse简介: argparse 模块是 Python 内置的一个用于命令项选项与参数 ...
- 4.1 C++ Boost 字符串处理库
Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量.可移植.高效的C应用程序.Boost库可以作为标准C库的后备,通常被称为准标准 ...
- 提升编码幸福感的秘密「GitHub 热点速览」
写代码是一个充满挑战的事情,在这段充满挑战的旅途中,我们都渴望找到那个提升幸福感的秘密.没准是更先进或是更快的工具,希望本期热点速递的开源项目,能给你带来启迪和乐趣,上菜! 第一个上场的是一款用 Ru ...
- Yarp 与 Nginx性能大比拼不出所料它胜利了!
Yarp 与 Nginx 性能大比拼 测试环境: Ubuntu 22.04.3 LTS (GNU/Linux 6.5.0-14-generic x86_64) Intel(R) Xeon(R) CPU ...