1 .对象的创建过程
Bird bird 创建的是一个 Bird 类型的引用,而 new Bird () 完成的是创建 Bird 对象,分配内存空间和初始化操作,然后将这个对象引用赋给 bird 变量,也就是建立 bird 变量与 Bird 对象的关联。
2 .从继承的角度分析对象的创建过程
在此我们以 Chicken 对象的创建为例,首先是字段,对象一经创建,会首先找到其父类 Bird ,并为其字段分配存储空间,而 Bird 也会继续找到其父类 Animal ,为其分配存储空间,依次类推直到递归结束,也就是完成 System.Object 内存分配为止。
思考
通过上面的讲述与分析,我们基本上对 .NET 在编译期的实现原理有了大致的了解,但是还有以下的问题,一定会引起一定的疑惑,那就是: Bird bird2 = new Chicken();
这种情况下, bird2.ShowType 应该返回什么值呢?而 bird2.type 又该是什么值呢?有两个原则,是 .NET 专门用于解决这一问题的:
关注对象原则: 调用子类还是父类的方法,取决于创建的对象是子类对象还是父类对象,而不是它的引用类型。例如 Bird bird2 = new Chicken() 时,我们关注的是其创建对象为 Chicken 类型,因此子类将继承父类的字段和方法,或者覆写父类的虚方法,而不用关注 bird2 的引用类型是否为 Bird 。 引用类型不同的区别决定了不同的对象在方法表中不同的访问权限。
注意: 根据关注对象原则,那么下面的两种情况又该如何区别呢?
Bird bird = new Chicken ();
Chicken chicken = new Chicken ();
根据我们上文的分析, bird 对象和 chicken 对象在内存布局上是一样的,差别就在于其引用指针的类型不同: bird 为 Bird 类型指针,而 chicken 为 Chicken 类型指针。以方法调用为例,不同的类型指针在虚拟方法表中有不同的附加信息作为标志来区别其访问的地址区域,称为 offset 。不同类型的指针只能在其特定地址区域内进行执行,子类覆盖父类时会保证其访问地址区域的一致性,从而解决了不同的类型访问具有不同的访问权限问题。
执行就近原则: 对于同名字段或者方法,编译器是按照其顺序查找来引用的,也就是首先访问离它创建最近的字段或者方法,例如上例中的 bird ,是 Bird 类型,因此会首先访问 Bird_type (注意编译器是不会重新命名的,在此是为区分起见),如果 type 类型设为 public ,则在此将返回 “ Bird ” 值。这也就是为什么在对象创建时必须将字段按顺序排列,而父类要先于子类编译的原因了。
// 经典指令解析之方法调度
using System;
using System.Collections.Generic;
using System.Text;
namespace testCall
{
public class Father
{
public void DoWork()
{
Console .WriteLine( "Father.DoWork()" );
}
public virtual void DoVirtualWork()
{
Console .WriteLine( "Father.DoVirtualWork()" );
}
public virtual void DoVirtualAll()
{
Console .WriteLine( "Father.DoVirtualAll()" );
}
}
public class Son : Father
{
public static void DoStaticWork()
{
Console .WriteLine( "Son.DoStaticWork()" );
}
public new void DoWork()
{
Console .WriteLine( "Son.DoWork()" );
}
public new virtual void DoVirtualWork()
{
base .DoVirtualWork();
Console .WriteLine( "Son.DoVirtualWork()" );
}
public override void DoVirtualAll()
{
Console .WriteLine( "Son.DoVirtualAll()" );
}
}
public class Grandson : Son
{
public override void DoVirtualWork()
{
base .DoVirtualWork();
Console .WriteLine( "Grandson.DoVirtualWork()" );
}
public override void DoVirtualAll()
{
base .DoVirtualAll();
Console .WriteLine( "Grandson.DoVirtualAll()" );
}
}
// 方法调用测试类
class call
{
static void Main( string [] args)
{
Father son = new Son ();
son.DoWork(); // 调用 Father.DoWork()
son.DoVirtualWork(); // 调用 Father.DoVirtualWork()
Son .DoStaticWork(); // 调用 Son 类特有的静态方法 DoStaticWork()
Father aGrandson = new Grandson ();
aGrandson.DoWork(); // 调用 Father.DoWork()
aGrandson.DoVirtualWork(); // 调用 Father.DoVirtualWork()
aGrandson.DoVirtualAll(); // 调用 Grandson.DoVirtualAll()
Console .ReadKey();
}
}
}
new 隐藏基类成员中同名的成员方法,也就是该方法独立于基类的方法。派生类同名方法如果未定义为 new 或 override 则默认定义为 new 的方法。
override 表示覆写,显式的重写基类成员,以实现派生类自己的版本。 C++ 派生类中对同名的基类方法的覆写只需将基类方法声明为 virtual 。
Son 中定义的 new DoWork() 独立于 Father 中定义的同名的 DoWork() 方法,对于 Son 对象继承的 DoWork() 方法被 new 隐藏而不可见。而 override DoVirtualAll() 则生成自己版本的 DoVirtualAll() 方法。
根据以上示意图很容易分析出以上测试代码的结果。
说明:
本文摘自《你必须知道的 .NET 》第十五回《继承本质论》,有改动。
http://www.cnblogs.com/anytao/archive/2007/09/10/must_net_15.html