在《CLR VIa C#》第三版 249页有这样一个例子:利用范型实现简单的链表,代码如下:
public class Node<T> { public T _data; public Node<T> _next; public Node(T data) : this(data,null) { } public Node(T data,Node<T> next){ this._data = data; this._next = next; } public override string ToString() { return _data.ToString() + (_next != null ? _next.ToString() : null); } } public class LeaderFunction { public void function() { Node<char> head = new Node<char>('A'); head = new Node<char>('B', head); head = new Node<char>('C', head); head = new Node<char>('D', head); head = new Node<char>('E', head); Console.WriteLine(head.ToString()); } }
执行结果是:EDCBA
一开始有点小郁闷:为啥呢?看官请看:head = new Node<char>('B', head);
这句话的意思就是,head重新定向,data=B,next指向上一个head。问题来了,你这个head是一个Node类型,当前的head指向新的node,node中的next指向上一个head,有点小乱,但归根结底问题就是,上个head都被别人抢走了,你还指向毛上个head
想了一会,联想到之前的拆箱和装箱规则,顿时豁然开朗
画个图,其实就明白了
虽然head指向了新的对象,但是原有对象在堆中并没有gc掉,仍让保存则托管堆某块内存中,这个地址保存在每个next中。这样上面的疑问就不存在了!
我们可以麻烦点,不要让堆中的对象处于悬浮状态,我们神明栈中的实例去指向他。就是这样
代码如下:
public class Node<T> { public T _data; public Node<T> _next; public Node(T data) : this(data,null) { } public Node(T data,Node<T> next){ this._data = data; this._next = next; } public override string ToString() { return _data.ToString() + (_next != null ? _next.ToString() : null); } } public class LeaderFunction { public void function() { Node<char> head = new Node<char>('A'); Node<char> head1 = new Node<char>('B', head); Node<char> head2 = new Node<char>('C', head1); Node<char> head3 = new Node<char>('D', head2); Node<char> head4 = new Node<char>('E', head3); Console.WriteLine(head4.ToString()); } }
其实两种代码写法,对应两种堆,和堆栈的分配方式。但是第一种分配方式显然要简单的多!