base到底指向了谁
base关键字在随笔《用《叩响C#之门》复习C#基础知识 第九章 面向对象编程:继承》叙述过一些,但自己试验后,发现表述不妥,拎出来单独分析下
每个对象都有一个指向自身的引用符即this,同样,可以通过base关键字来访问基类成员。
base常用于1)在派生类对象初始化时和基类进行通信(即调用派生类构造函数前调用基类构造函数);2)访问基类的公有成员和受保护成员,私有成员不可访问;同时,base用来调用基类中已被派生类中方法重写的方法(是调用基类中的方法,不象上篇随笔中,基类应用符访问方法,结果访问到派生类中重写后的方法)。
针对第2)点分析如下:
自己分析的:base应该被看做是调用base关键字的方法所属的类型的最直接的基类型对象的引用(虽然base其实不能作为引用符来赋值)
由此可见,派生类对象生成后,实际上在堆中的低内存存储着基类的成员,相当于整个派生类对象包含着某个基类对象。(这样理解,真是把自己吓一跳,但,目前感觉是对的)base实际上是指向:内存中 调用base的方法所属类型B的最直接的基类A所包含的成员(可将此部分内存内容看做是A的对象)
1)针对基类的字段,通过base去访问托管堆中的GC堆的基类字段。
2)针对基类的方法,通过base去访问托管堆中的Loader Heap堆中的基类方法表。
真正使用时,引用符及所调用的方法,规则遵循上篇随笔《针对基类引用符指向派生类对象引起的思考》中的描述。
注意:this和base关键字都不能用于静态方法中(因为都是放在对象的方法中使用的),也就是说在Main()方法中也不可以使用。
以练习来说明:
using System; using System.Collections.Generic; namespace Test { public class Animal { protected string name; public virtual string Name { get { return "调用自Animal的Name " + this.name; } set { this.name = value; } } public virtual void DisplayName() { Console.WriteLine("Animal类传下的Display方法"); } public virtual void ReturnBase()//检查各类使用base的情况 { Console.WriteLine("调用Animal的ReturnBase() "); } } public class Vertebrata : Animal { public override string Name//对属性Name进行了覆写 { get { return "调用自Vertebrata的Name " + this.name; } set { this.name = value; } } public override void DisplayName()//对方法DisplayName()进行了覆写 { Console.WriteLine("Vertebrata类传下的Display方法"); } public override void ReturnBase()//检查各类使用base的情况 { Console.Write("调用Vertebrata的ReturnBase() "); base.DisplayName(); } } public class Mammal : Vertebrata { public override string Name { get { return "调用自Mammal的Name " + this.name; } set { this.name = value; } } public override void DisplayName() { Console.WriteLine("Mammal类传下的Display方法"); } public override void ReturnBase()//检查各类使用base的情况 { Console.Write("调用Mammal的ReturnBase() "); base.DisplayName(); } } public class Cat : Mammal { public override string Name { get { return "调用自Cat类的Name " + this.name; } set { this.name = value; } } public override void ReturnBase()//检查各类使用base的情况 { Console.Write("调用Cat的ReturnBase() "); base.DisplayName(); } } public class Program { static void Main() { Animal ani1 = new Cat(); Animal ani2 = new Mammal(); Animal ani3 = new Vertebrata(); Animal ani4 = new Animal(); Vertebrata vert1 = new Cat(); Vertebrata vert2 = new Mammal(); Vertebrata vert3 = new Vertebrata(); Mammal mam1 = new Cat(); Mammal mam2 = new Mammal(); Cat cat1 = new Cat(); ani1.ReturnBase(); ani2.ReturnBase(); ani3.ReturnBase(); ani4.ReturnBase(); Console.WriteLine(); vert1.ReturnBase(); vert2.ReturnBase(); vert3.ReturnBase(); Console.WriteLine(); mam1.ReturnBase(); mam2.ReturnBase(); Console.WriteLine(); cat1.ReturnBase(); } } }
输出:
此段代码实际上是将上篇随笔的规则也体现出来了,输出左边部分为不同类型引用符访问同名函数时,访问的规则。输出右边部分为方法调用base后的结果。
显然,当引用符类型为基类A,A的派生类为B,而此时引用符指向的是B的派生类C的对象,先是找到应当访问的函数(如此函数在C中,即C中重写了A中的虚函数),当该函数中含base关键字时,base则访问此函数对应类的直接基类(B)所对应的内存中B类的成员(即可将内存中这部分内容看作为B的对象,因为C的对象可以看做包含着B的对象,B的对象包含着A的对象,统筹创建成了C的对象)。综上,如此说来,倒可以牵强地将base看做为指向基类对象的引用符了。
如果将上述代码中Cat类中的ReturnBase()隐藏掉,输出结果如下:
按此篇随笔的内容,应该不难分析出输出的结论了