代码改变世界

由类能否包含自己说开去

2010-12-13 16:47  MichaelYin  阅读(2203)  评论(15编辑  收藏  举报

下午在Coding的时候突然想到了一个问题,类到底能不能包含自己?在什么情况下能包自己?

当时正在实现一个类似链表的功能,在一个节点中需要有下个节点的引用的数据,比如像这样子的代码。

    public class Node
    {
        public int data;
        public Node Next;
    }

在Node中有一个Node类型的引用地址,用来找到这个节点的下一个节点。默认构造函数调用后会将data置0,Next置null,当时我写到这里突然想起来好像在哪里看到过类中包含类自己是不行的。于是在Console中跑了一遍,运行结果是对的。断点显示初始化的值是我预计的结果。那到底在什么情况下类不能包含自己呢?要知道答案,我们还是先要说说初始化背后的故事。

在我初始化节点的时候我调用的是new Node()这个方法,这个时候.Net会首先计算类中成员需要的内存空间,这里就是int类型和Node引用类型的地址的空间(实际还有别的,不过和我们讨论的问题无关,故在这里省略),然后在内存中分配内存空间,分配了内存空间后就会对类中的变量进行初始化,比如我的代码中写的是public int data = 3;那么data这个变量就会在这时被赋值3,就变量初始化后然后会调用类的相应的构造函数,因为我这里没有实现构造函数,所以最后的值就是data的值为0,Node的值为null(引用类型的默认初始化值都是null).

到这里我们已经把类的初始化过程弄的比较清楚了,现在要做的就是如何改变代码使程序异常。我们把注意力放到public Node Next; 这一句上,这里我没有加new Node(),这个方法,所以Next的值为0,现在我们在后面把初始化类的代码加上。就像这样

    public class Node
    {       
        public int data;
        public Node Next = new Node();
    }

好了,现在让我们来想一下Next这个地方的值是怎么样的,data置0后,new Node需要返回给Next一个内存中的地址,所以它需要再次运行类的初始化的代码,在内存中重新开辟区域,给data置0,然后里面显然也有一个Node类型的引用,当给data赋值后,我也需要给那个Next赋值,然后又需要在内存中开辟区域。这样永无止境,直到内存耗而报错。

到这里关于类能否包含自己相信我们已经找到了答案了。另外需要在提的一点就是如果类中的自己是静态变量呢?静态变量是属于类级别的,它的初始化是在类初始化的时候,如果没有调用构造函数,变量置null,如果调用了构造函数,则按构造函数进行初始化。如果构造函数中出现了上面的这种情况,一样的会报异常。

类中能否包含自己听来多是一种经验,但是如果你真正弄清楚了里面的过程以及为什么为异常的原因,相信你犯错的机会会小的多。