You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:27:14