C#自定义队列引用赋值疑问:为何_last更新不影响_first?
C#自定义队列Add方法中引用赋值逻辑解析
我在调试一段C#自定义队列代码时,对变量的赋值逻辑感到困惑:
- 第一次调用
Add()时,_first为null,会被赋值为_last,此时二者指向同一个节点。 - 第二次调用
Add()时,_last不为null,新节点会被赋值给_last.Next,由于_first和_last指向同一个节点,_first.Next也会同步更新。 - 但执行
_last = t;时,只有_last指向了新节点,_first却依然指向原来的节点,这是为什么?
自定义队列代码
public class QueueCustom<T> { public class QueueNode<T> { public T Data; public QueueNode<T>? Next; public QueueNode(T data) { this.Data = data; } } private QueueNode<T>? _first; private QueueNode<T>? _last; public void Add(T item) { var t = new QueueNode<T>(item); if (_last != null) { _last.Next = t; // 此时_first和_last指向同一个节点,_first.Next也会被更新 } _last = t; // 为什么只有_last指向新节点,_first不会跟着变? _first ??= _last; } public T Remove() { if (_first == null) { throw new InvalidOperationException("Queue is empty"); } T data = _first.Data; _first = _first.Next; if (_first == null) { _last = null; } return data; } }
调用代码
var queue = new QueueCustom<int>(); queue.Add(1); queue.Add(2); queue.Add(3);
核心原因:引用变量和对象的区别
在C#中,_first和_last都是引用类型变量,它们本身存储的是堆上对象的内存地址,而非对象本身。
第一次Add后的状态:
创建第一个节点QueueNode(1),_last被赋值为这个节点,然后_first ??= _last让_first也指向同一个节点。此时_first和_last的内存地址值完全相同,都指向堆上的同一个节点对象。第二次Add时的
_last.Next = t:
这行代码是修改堆上节点对象的属性——通过_last找到它指向的节点,把该节点的Next属性设置为新创建的t(QueueNode(2))。因为_first指向的是同一个节点对象,所以_first.Next自然也会看到这个变化,本质是同一个对象的属性被修改了。执行
_last = t的本质:
这行代码是修改_last这个引用变量本身的值——把_last存储的内存地址替换成新节点t的地址。而_first这个引用变量存储的还是原来第一个节点的地址,所以它不会指向新节点。
简单类比:把_first和_last看成两张纸条,上面写着箱子的位置。第一次Add后,两张纸条都写着第一个箱子的位置。修改箱子里的东西(_last.Next = t),不管看哪张纸条找过去,箱子里的内容都变了。但如果把其中一张纸条上的地址改成第二个箱子的位置(_last = t),另一张纸条上的地址还是第一个箱子的,自然不会指向新箱子。
内容的提问来源于stack exchange,提问作者Josh
相关产品推荐
相关产品推荐

