赶项目时发现了一个问题,定义一个引用对象,如果在循环外定义对象,在循环内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,出现数据相同现象? 引发对引用类型和值类型的底层逻辑的思考。的更多相关文章

  1. phalcon: 当删除循环删除一组数据,需要判断影响的行affectedRows

    phalcon:有一个表,按日期查找半年以为的数据,由于数据量特别大,不能一次:delete删除数据,否则会造成数据表卡顿,数据库锁死. 那么只能循环的删除数据,每次删除100条左右,知道删除为止., ...

  2. Oracle游标-循环查询表中数据(表名),并执行

    Oralce 表中存有一个字段,该字段存储表名,要把该表中的所有表名查询出来(即表名结果集),且执行结果集from 表名结果集: declare v_ccount ); --定义一个游标变量 curs ...

  3. JS 循环遍历JSON数据 分类: JS技术 JS JQuery 2010-12-01 13:56 43646人阅读 评论(5) 收藏 举报 jsonc JSON数据如:{&quot;options&quot;:&quot;[{

    JS 循环遍历JSON数据 分类: JS技术 JS JQuery2010-12-01 13:56 43646人阅读 评论(5) 收藏 举报 jsonc JSON数据如:{"options&q ...

  4. SQL SERVER 游标循环读取表数据

    [cursor]游标:用于循环表行数据,类似指针 格式如下: declare tempIndex cursor for (select * from table) --定义游标 open tempIn ...

  5. 高效遍历匹配Json数据与双层for循环遍历Json数据

    工作中往往遇到这种情况,保留用户操作痕迹,比如用户选择过得东西,用户进入其它页面再返回来用户选择的的数据还在. 比如:1.购物车列表中勾选某些,点击任意一项,前往详情页,再返回购物车依旧需要呈现勾选状 ...

  6. Ajax请求php返回json对象数据中包含有数字索引和字符串索引,在for in循环中取出数据的顺序问题

    //php中的数组数据格式 Array ( [all] => Array ( [title] => ALL [room_promotion_id] => all ) [best_av ...

  7. js循环读取json数据,将读取到的数据用js写成表格

    ①js循环读取json数据的方式: var data=[{"uid":"2688","uname":"*江苏省南菁高级中学 022 ...

  8. Vue之循环遍历Json数据,填充Table表格

    简单记一次Vue循环遍历Json数据,然后填充到Table表格中,展示到前端的代码: async getData(id) { const res = await this.$store.api.new ...

  9. 使用pymysql循环删除重复数据,并修改自增字段偏移值

    创建表: CREATE TABLE `info` ( `id` tinyint NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL, PRIMAR ...

  10. 生成二维码 加密解密类 TABLE转换成实体、TABLE转换成实体集合(可转换成对象和值类型) COOKIE帮助类 数据类型转换 截取字符串 根据IP获取地点 生成随机字符 UNIX时间转换为DATETIME\DATETIME转换为UNIXTIME 是否包含中文 生成秘钥方式之一 计算某一年 某一周 的起始时间和结束时间

    生成二维码 /// <summary>/// 生成二维码/// </summary>public static class QRcodeUtils{private static ...

随机推荐

  1. [转帖]linux下如何避免rsyslog系统日志不停打印到console

    背景:linux环境下,服务器由于某种异常导致rsyslog message不停打印到console控制台,影响我们正常使用. ps:我遇见的场景: 解决办法:1. vim /etc/rsyslog. ...

  2. Core 文件的简单学习

    背景 最近公司内经常出现jvm进程宕机的情况. 宕机之后没有产生jvm的dump文件.比如xxx.hprof 但是产生了 core.$pid的文件. 曾经在aarch64架构上宕机时曾经想学习一下co ...

  3. Oracle数据库权限学习--表或者是视图不存在

    Oracle数据库权限学习--表或者是视图不存在 摘要 本文写于: 12.10 01:00 巴西踢的太烂了 帮同事看一下补丁执行报错的问题. 问题原因很简单. user_all_table能够后去本用 ...

  4. Harbor简单搭建以及异常排查的过程与思路

    Harbor简单搭建以及异常排查的过程与思路 前言 我发现我总是能够遇到别人遇不到的问题. 本来搭建十分钟就可以搭建完成 结果我硬生生的搭建了四十分钟. 为了保证下次不再浪费时间. 这里加单总结一下遇 ...

  5. 《Javascript高级程序设计》读书笔记——函数与闭包

    函数与闭包 函数创建 创建函数有两种方式,第一种是函数声明.函数声明有一个很重要的特征就是函数声明提升(function declaration hoisting),意思是在执行代脚本前会先读取所有的 ...

  6. 【小实验】javascript 能够表述的最大整数

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 打开浏览器的控制台,开始输入数值: 输入:(16 位十进制 ...

  7. mysql系列14---mysql数据库还原与备份

    一.Liunx服务器下数据库定时备份 1.编写mysql在docker容器中备份的shell脚本: #!/bin/bash#  2020-11-15#docker启动的mysql备份mysql_use ...

  8. Milvus性能优化提速之道:揭秘优化技巧,避开十大误区,确保数据一致性无忧,轻松实现高性能

    Milvus性能优化提速之道:揭秘优化技巧,避开十大误区,确保数据一致性无忧,轻松实现高性能 Milvus 是全球最快的向量数据库,在最新发布的 Milvus 2.2 benchmark中,Milvu ...

  9. 解锁搜索新境界!让文本语义匹配助你轻松找到你需要的一切!(快速上手baseline)

    解锁搜索新境界!让文本语义匹配助你轻松找到你需要的一切!(快速上手baseline) 实现了多种相似度计算.匹配搜索算法,支持文本.图像,python3开发,pip安装,开箱即用. 文本相似度计算(文 ...

  10. 提升编码幸福感的秘密「GitHub 热点速览」

    写代码是一个充满挑战的事情,在这段充满挑战的旅途中,我们都渴望找到那个提升幸福感的秘密.没准是更先进或是更快的工具,希望本期热点速递的开源项目,能给你带来启迪和乐趣,上菜! 第一个上场的是一款用 Ru ...