当前位置: 首页 > news >正文

C# 修改基类List中某一元素的子类类型

描述:

基类:BaseClass

子类1:A

子类2:B

然后我有一个List<BaseClass>类型的链表:list,我先往list中添加了两个元素:

第一个元素为A类型,第二个元素为B类型,

然后我想改变第一个元素类型由原来的A到B;

直接使用list.ElementAt(0)取出第一个元素a,然后new 了一个新的B对象,因为我以为a的引用和链表list的第一个位置list[0]的引用相同,实际上并不是这样的。

如果不new新对象,改变a里面的属性,会直接反馈到list[0]里面,但是new 了新对象的话,a和list[0]就不是同一个东西了;

内存部分分析:

1. 初始状态(执行前)

var a = new A();      // 创建A实例
var b = new B();      // 创建B实例
var list = new List<BaseClass>();  // 创建列表实例

堆栈 (Stack) 内存:

地址    变量名    值 (堆地址)
0x1000  a        → 0x2000
0x1004  b        → 0x3000  
0x1008  list     → 0x4000

堆 (Heap) 内存:

地址      对象类型    数据
0x2000    A实例      {StrBase=null, StrA=null}
0x3000    B实例      {StrBase=null, StrB=null}  
0x4000    List实例   [容量=4, 元素数组地址=0x5000]
0x5000    数组       [0x2000, null, null, null] ← 第一个元素指向A实例

2. 添加元素后

list.Add(a);
list.Add(b);

堆栈 (Stack) 内存:(不变)

地址    变量名    值 (堆地址)
0x1000  a        → 0x2000
0x1004  b        → 0x3000  
0x1008  list     → 0x4000

堆 (Heap) 内存:

地址      对象类型    数据
0x2000    A实例      {StrBase=null, StrA=null}
0x3000    B实例      {StrBase=null, StrB=null}  
0x4000    List实例   [容量=4, 元素数组地址=0x5000, 计数=2]
0x5000    数组       [0x2000, 0x3000, null, null] ← 第一个指向A,第二个指向B

3. 获取元素引用

var aa = list.ElementAt(0);

堆栈 (Stack) 内存:

地址    变量名    值 (堆地址)
0x1000  a        → 0x2000
0x1004  b        → 0x3000  
0x1008  list     → 0x4000
0x100C  aa       → 0x2000  ← 新的变量aa,指向A实例

堆 (Heap) 内存:(不变)

地址      对象类型    数据
0x2000    A实例      {StrBase=null, StrA=null}
0x3000    B实例      {StrBase=null, StrB=null}  
0x4000    List实例   [容量=4, 元素数组地址=0x5000, 计数=2]
0x5000    数组       [0x2000, 0x3000, null, null]

4. 关键操作:重新赋值

aa = new B();  // 创建新的B实例

堆栈 (Stack) 内存:

地址    变量名    值 (堆地址)
0x1000  a        → 0x2000
0x1004  b        → 0x3000  
0x1008  list     → 0x4000
0x100C  aa       → 0x6000  ← aa现在指向新的B实例!

堆 (Heap) 内存:

地址      对象类型    数据
0x2000    A实例      {StrBase=null, StrA=null}
0x3000    B实例      {StrBase=null, StrB=null}  
0x4000    List实例   [容量=4, 元素数组地址=0x5000, 计数=2]
0x5000    数组       [0x2000, 0x3000, null, null] ← 第一个元素仍然指向A实例!
0x6000    B实例      {StrBase=null, StrB=null}   ← 新创建的B实例

关键点说明

1. 引用类型的本质

  • 变量存储在堆栈ablistaa 这些变量都存储在堆栈中

  • 对象存储在堆中:实际的A、B实例和List实例存储在堆中

  • 变量存储的是引用:堆栈中的变量存储的是堆中对象的地址

2. 赋值操作的含义

aa = new B();

这条语句的实际作用是:

  1. 在堆中创建新的B实例(地址0x6000)

  2. 将堆栈中变量aa的值从0x2000改为0x6000

  3. 不影响列表内部数组中存储的引用值

3. 正确的修改方式

如果要修改列表中的元素,应该:

// 方式1:直接修改列表元素
list[0] = new B();  // 这会修改数组[0]位置的引用// 方式2:通过引用修改
ref var elementRef = ref list[0];
elementRef = new B();

4. 内存变化对比

错误的方式(你的代码):

堆栈: aa → 0x6000 (新B实例)
堆:   数组[0] → 0x2000 (原来的A实例) ← 未改变!

正确的方式:

堆栈: aa → 0x6000 (新B实例)  
堆:   数组[0] → 0x6000 (新B实例) ← 已改变!

总结

从堆栈内存的角度看:

  • aa = new B() 只改变了堆栈中变量aa存储的地址值

  • 列表的内部数组存储在堆中,其元素引用保持不变

  • 要修改列表内容,必须直接操作列表本身,而不是操作从列表获取的临时变量

测试代码:

static void Main(string[] args)
{Console.WriteLine("Hello, World!");var a = new A();a.StrBase = "A";var b = new B();//var list = new List<BaseClass>();var list = new BaseClass[2];list[0]=a;list[1]=b;//list.Add(b);//var aa = list.ElementAt(0);var aa = list.FirstOrDefault(s => s.StrBase.Equals("A"));aa = new B();//aa.StrBase =" new B()";int c = 0;
}
public class A : BaseClass
{ 
public string StrA {  get; set; }
}
public class B : BaseClass
{
public string StrB {  get; set; }
}
public class BaseClass
{public string StrBase { get; set; }
}


文章转载自:

http://C19OhlpD.fgsms.cn
http://GVM6TT6H.fgsms.cn
http://zwnzX9WC.fgsms.cn
http://2t4ZKart.fgsms.cn
http://UOfp1IXH.fgsms.cn
http://izfVaUBY.fgsms.cn
http://AjfPGCFt.fgsms.cn
http://OVs9wmEA.fgsms.cn
http://6dUplXCg.fgsms.cn
http://aERJWNyY.fgsms.cn
http://JhE4CyN0.fgsms.cn
http://G2gKkdgs.fgsms.cn
http://QeeV5Gpl.fgsms.cn
http://Xz2jlU3q.fgsms.cn
http://HpuI6uR9.fgsms.cn
http://LGsfLf96.fgsms.cn
http://6jDSCmHm.fgsms.cn
http://kX5hY9Em.fgsms.cn
http://7jNDSdzi.fgsms.cn
http://vp8emZW6.fgsms.cn
http://ursbh9WH.fgsms.cn
http://2rXutnBi.fgsms.cn
http://3HibjdTw.fgsms.cn
http://7PWXmcGG.fgsms.cn
http://3kZ5NCxY.fgsms.cn
http://V04lOFWa.fgsms.cn
http://lAWOo9e5.fgsms.cn
http://bF5jjosx.fgsms.cn
http://PmRpZeeW.fgsms.cn
http://A08KHwO6.fgsms.cn
http://www.dtcms.com/a/366189.html

相关文章:

  • 如何在 iPhone 或 iPad 上删除文件
  • MongoDB 高可用部署:Replica Set 搭建与故障转移测试
  • MacOS微信双开,亲测有效
  • MySQL事务的四大特性(ACID)
  • 数说故事 | 2025年运动相机数据报告,深挖主流品牌运营策略及行业趋势​
  • K8S容器POD内存快照导出分析处理方案
  • 【面试题】Prompt是如何生成的,优化目标是什么,任务是什么?
  • Elasticsearch 备份和恢复
  • 软考中级习题与解答——第二章_程序语言与语言处理程序(2)
  • RTC实时时钟RX8025SA国产替代FRTC8025S
  • git基础命令 git基础操作
  • 2025市面上比较实用的财会行业证书,最值得考的8个职业证书推荐
  • 开源与定制化直播电商系统源码对比:如何选择开发方案?
  • Spring 事务提交成功后执行额外逻辑
  • Attention-Based Map Encoding for Learning Generalized Legged Locomotion
  • MMD动画(二)动作制作
  • Hoppscotch:开源轻量API测试工具,秒启动高效解决临时接口测试需求
  • 【机器学习】HanLP+Weka+Java算法模型
  • 算法随笔(一)
  • Electron 执行python脚本
  • Dubbo(分布式RPC调用和分布式文件储存)
  • 如何简单理解状态机、流程图和时序图
  • 成为一个年薪30W+的FPGA工程师是一种什么体验?
  • 进程与线程详解, IPC通信与RPC通信对比,Linux前台与后台作业
  • 在国企干了 5 年 Java,居然不知道 RPC?这正常吗?
  • VU9P板卡设计方案:基于VU9P的32@ SFP28+4@ QSFP28路光纤交换板卡
  • Zynq开发实践(FPGA之uart发送)
  • 如何在 IntelliJ IDEA 中进行全局替换某个字段(或文本)
  • 案例精述 | 防护即智能 Fortinet赋能英科全栈安全重构实践
  • React学习之路永无止境:下一步,去向何方?