【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 ...
随机推荐
- Java进程 OOM的多种情况
Java进程 OOM的多种情况 摘要 OOM 其实有多种: 第一类是JVM原生自发处理的, 这种也分为多种情况. 1. 堆区使用了比较多,并且大部分对象都还有引用, GC不出来可用内存, 这是要给对象 ...
- PHP GC回收机制详解
前言 GC的全称是Garbage Collection也就是垃圾回收的意思,在PHP中,是使用引用计数和回收周期来自动管理内存对象的,当一个对象被设置为NULL,或者没有任何指针指向时,他就会变成垃圾 ...
- ToneGenerator Init failed Crash 崩溃
需求需要在扫码时产生一个短促的提示音, 搜了下像这样实现.测试时发现多次扫码后,会触发程序崩溃问题. 异常如下 java.lang.RuntimeException: Init failed at a ...
- css伪类和伪元素在项目中的使用-红色*显示
CSS使用伪类给表单添加星号 <style type="text/css"> .form-item label::before { content: '*'; colo ...
- vue中使用Object.assign导致视图不响应
可以正常响应的 <template> <div> <ul> <li class="li-item" v-for="(item,i ...
- vscode搜索框不见了如何显示
- P9989 [Ynoi Easy Round 2023] TEST_69 题解
题目链接: [Ynoi Easy Round 2023] TEST_69 首先GCD有比较良好的一些性质.我们观察到一次 \(GCD(a_i,x)\) 操作,会有以下两种变化. 如果 \(x \bmo ...
- 零基础入门学习Java课堂笔记 ——day05
面向对象(上) 面向过程:我打算列个计划表一步一步来 面向对象:我喜欢先分析分类,把复杂的问题简单化 1.什么是面向对象!!? 面向对象的本质就是:以类的方式组织代码,以对象的方式组织数据 封装 继承 ...
- 【Java并发入门】01 并发编程Bug的源头
一.根本原因 「CPU.内存.磁盘之间的速度差异」 为了能同时执行多个任务,CPU 发展出时间片轮转.多核等 CPU 要从内存中读数据太慢了,所以给自己设置了缓存 CPU 读磁盘更慢了,所以可以让该线 ...
- Java微服务SpringCloud+Uniapp+Vue3+Element Plus开源BizSpring商城
产品介绍 BizSpring电商平台概述 BizSpring电商平台,是基于最新Spring Cloud 微服务架构开发的多语言电商平台,使用领先的 Vue3.0+ElementPlus + unia ...