.net知识和学习方法系列(十五)类型,对象,堆栈和托管堆

桂素伟

  1. 类型,对象,堆栈和托管堆

C#的类型和对象在应用计算机内存时,大体用到两种内存,一个叫堆栈,另一个叫托管堆,下面我们用直角长方形来代表堆栈,用圆角长方形来代表托管堆。

首先讨论一下方法内部变量的存放。

先举个例子,有如下两个方法, Method_1和 Add,分别如下:

public void Method_1()
{
 int value1=10;  //1
 int value2=20;    //2
 int value3=Add(value,value); //3 
}
public int Add(int n1,int n2)//4
{
   rnt sum=n1+n2;//5
   return sum;//6
}

这段代码的执行,用图表示为:

上述的每个图片,基本对应程序中的每个步骤。在开始执行 Method_1的时候,先把 value1压入堆栈顶,然后是 value2,接下来的是调用方法 Add,因为方法有两个参数是 n1和 n2,所以把 n1和 n2分别压入堆栈,因为此处是调用了一个方法,并且方法有返回值,所以这里需要保存 Add的返回地址,然后进入 Add方法内部,在 Add内部,首先是给 sum赋值,所以把 sum压入栈项,然后用 return返回,此时,先前的返回地址就起到了作用, return会根据地址返回去的,在返回的过程中,把 sum推出栈顶,找到了返回地址,但在 Method_1方法中,我们希望把 Add的返回值赋给 value3,此时的返回地址也被推出堆栈,把 value3压入堆栈。虽这个例子的结果在这里没有多大用途,但这个例子很好的说明了在方法被执行时,变量与进出堆栈的情况。这里也能看出为什么方法内部的局变量用过后,不能在其他方法中访问的原因。

其次来讨论一下类和对象在托管堆和堆栈中的情况。

先看一下代码:

class Car
    {
        public void Run()
        {
            Console.WriteLine("一切正常");
        }
        public virtual double GetPrice()
        {
            return 0;
        }
        public static void Purpose()
        {
            Console.WriteLine("载人");
        }
    }
    class BMW : Car
    {
        public override double GetPrice()
        {
            return 800000;
        }
}

上面是两个类,一个 Father一个Son,Son继承了Father,因为你类中有一个virtual的BuyHouse方法,所以Son类可以重写这个方法。

下面接着看调用代码。

public void Method_A()
{
            double CarPrice;//1
            Car car = new BMW();//2
            CarPrice = car.GetPrice();//调用虚方法(其实调用的是重写后的方法)
            car.Run();//调用实例化方法
            Car.Purpose();//调用静态方法
 }

这个方法也比较简单,就是定义一个变量用来获得价格,同时定义了一个父类的变量,用子类来实例化它。

接下来,我们分步来说明。

看一下运行时堆栈和托管堆的情部我:

 

这里需要说明的是,类是位于托管堆中的,每个类又分为四个类部,类指针,用来关联对象;同步索引,用来完成同步 (比如线程的同步 )需建立的;静态成员是属于类的,所以在类中出现,还有一个方法列表(这里的方法列表项与具体的方法对应)。

当 Method_A方法的第一步执行时:  

这时的 CarPrice是没有值的

当 Method_A方法执行到第二步,其实第二步又可以分成

Car car;

car = new BMW ();

先看 Car car;

 

car在这里是一个方法内部的变量,所以被压到堆栈中。

再看 car = new BMW ();

这是一个实例化过程, car变成了一个对象

 

这里是用子类来实例化父类型。对象其实是子类的类型的,但变量的类型是父类的。

接下来,在 Method_A中的调用的中调用 car.GetPrice(),对于 Car来说,这个方法是虚方法(并且子类重写了它),虚方法在调用是不会执行类型上的方法,即不会执行 Car类中的虚方法,而是执行对象对应类上的方法,即  BMW中的 GtPrice。

如果 Method_A中执行方法 Run(),因为 Run是普通实例方法,所以会执行 Car类中的 Run方法。

如果调用了 Method_A的 Purpose方法,即不用变量car调用,也不用对象调用,而是用类名Car调用,因为静态方法会在类中分配内存的。如果用Car生成多个实例,静态成员只有一份,就是在类中,而不是在对象中。

 

 

下一篇:(桂素伟).net知识和学习方法系列(十六)CLR-托管理程序和它的运行