C#中重新实例化对象为何导致其他对象持有的原引用失效
核心原因
问题本质是混淆了C#引用类型的三个核心概念:引用类型变量、变量存储的引用地址、堆上的实际实例对象。ObjectHolder从始至终持有的都不是变量b本身,而是初始化传入参数时,b当时指向的堆对象的内存地址。b和objectHolder.passedObject是两个完全独立的存储位置,仅在初始化阶段刚好存储了相同的地址值。
逐行执行逻辑拆解
- 执行
PassedObject b = new PassedObject(20);:堆内存中创建一个number=20的PassedObject实例,栈上的变量b存储该实例的内存地址,指向这个堆对象。 - 执行
new ObjectHolder<PassedObject>(b):构造函数把传入的地址值赋值给自身的passedObject字段,此时b和objectHolder.passedObject是两个独立变量,存储了相同的地址,指向同一个堆对象。 - 执行
b.number = 888;:顺着b存储的地址找到堆上的实例,修改实例的number属性值。因为objectHolder.passedObject也指向同一个实例,所以第二次打印输出888。 - 执行
b = new PassedObject(7782);:堆内存中新建一个number=7782的PassedObject实例,仅修改变量b存储的地址值,让b指向新实例。这一步完全不会改动objectHolder.passedObject存储的地址,它依然指向最早创建的那个、number已经被改为888的旧实例。 - 执行
b.number = 2;:修改的是b当前指向的新实例的属性值,和objectHolder持有的旧实例没有任何关联,所以后续打印永远输出旧实例的888。
通俗类比:变量
b就像一张写着家庭住址的便签,初始化holder时你把便签上的地址抄了一份存在holder里。之后你把自己手里便签的地址涂改成新家的地址,存在holder里的那份抄录地址不会自动更新,顺着旧地址找过去当然还是原来的房子。
测试代码
PassedObject b = new PassedObject(20); ObjectHolder<PassedObject> objectHolder = new ObjectHolder<PassedObject>(b); objectHolder.PrintData(); // prints 20 b.number = 888; objectHolder.PrintData(); // prints 888 b = new PassedObject(7782); objectHolder.PrintData(); // prints 888 b.number = 2; objectHolder.PrintData(); // still 888, lost reference... public class PassedObject { public int number { get; set; } public PassedObject(int number) { this.number = number; } } public class ObjectHolder<T> where T : PassedObject { public T passedObject; public ObjectHolder(T newObject) { passedObject = newObject; } public void PrintData() { Console.WriteLine(passedObject.number); } }
内容的提问来源于stack exchange,提问作者eskevv
相关产品推荐
相关产品推荐

