Delphi.NET 内部实现分析(3.2)
首先我们来看看元类的定义与实现
//-----------------------------------------Borland.Delphi.System.pas--
type
TObject = System.Object;
_TClass = class;
TClass = class of TObject;
_TClass = class
protected
FInstanceType: System.RuntimeTypeHandle;
FClassParent: _TClass;
public
constructor Create; overload;
constructor Create(ATypeHandle: System.RuntimeTypeHandle); overload;
constructor Create(AType: System.Type); overload;
function ClassParent: TClass; virtual;
end;
TClassHelperBase = class(TObject)
public
FInstance: TObject;
end;
//-----------------------------------------Borland.Delphi.System.pas--
上一节我们大概分析过元类的实现原理。每一个类有一个对应的嵌套子类,
名称为@Meta前缀加上类名,此类从Borland.Delphi.System._TClass类继承出来,
在实现上类似TClassHelperBase,只不过FInstance是类一级的静态成员变量。
.class /*0200000D*/ auto ansi nested public beforefieldinit @MetaTDemo
extends Borland.Delphi.System.@TClass/* 02000003 */
{
.field /*0400000E*/ public static class HelloUnit.TDemo/* 02000005 *//@MetaTDemo/* 0200000D */ @Instance
.method /*06000027*/ private hidebysig specialname rtspecialname static
void .cctor() cil managed
.method /*06000026*/ public hidebysig specialname rtspecialname
instance void .ctor() cil managed
.method /*06000008*/ public instance void
Hello() cil managed
}
元类的静态构造函数如上一节中的@MetaTDemo..cctor()函数,构造一个元类的实例
放入元类的@Instance静态成员
private static TDemo.@MetaTDemo..cctor() {
TDemo.@MetaTDemo.@Instance = new @MetaTDemo..ctor();
}
元类的构造函数则载入自己类型的Token到FInstanceType字段中。
public TDemo.@MetaTDemo..ctor() : base() {
this.FInstanceType = Token of TDemo;
}
这个Token在CLR中起到索引的作用,用以在Metadata诸多表中定位特定的表项,
每种元素如类、方法、字段、属性等等都有其自己的Token。在BCL中Token表现为
RuntimeTypeHandle类型,用于诸如Type.GetTypeFromHandle之类函数,
可以通过Type.TypeHandle获取。
关于Token的重要性及物理实现上用法请参加前面提到的我对Metadata分析的文章。
元类这里保存的FInstanceType实际上就是类似于Delphi中VMT指针的索引。
//-----------------------------------------Borland.Delphi.System.pas--
constructor _TClass.Create;
begin
inherited Create;
end;
constructor _TClass.Create(ATypeHandle: System.RuntimeTypeHandle);
begin
inherited Create;
FInstanceType := ATypeHandle;
end;
constructor _TClass.Create(AType: System.Type);
begin
Create(AType.TypeHandle);
end;
//-----------------------------------------Borland.Delphi.System.pas--
可以看到_TClass的几种形式构造函数,实际上都是围绕着这个Token做文章。
//-----------------------------------------Borland.Delphi.System.pas--
function _TClass.ClassParent: TClass;
begin
if not Assigned(FClassParent) then
FClassParent := _TClass.Create(System.Type.GetTypeFromHandle(FInstanceType).BaseType.TypeHandle);
Result := FClassParent;
end;
//-----------------------------------------Borland.Delphi.System.pas--
而_TClass.FClassParent则是在需要时填充的父类的元类。注意这里的转换Token到
CLR中类型的方法。System.Type.GetTypeFromHandle函数和后面要使用到的
System.Type.GetTypeHandle函数完成Type与Token之间的双向转换。
因此可以说在Delphi.NET中,元类实际上就是对类的Token的一个封装。
在了解了元类的实现后,我们来看看如何从一个Token获取其元类。
//-----------------------------------------Borland.Delphi.System.pas--
var
MetaTypeMap: Hashtable;
function _GetMetaFromHandle(ATypeHandle: System.RuntimeTypeHandle): _TClass;
var
t: System.Type;
begin
if not Assigned(MetaTypeMap) then
MetaTypeMap := Hashtable.Create;
Result := _TClass(MetaTypeMap[ATypeHandle]);
if not Assigned(Result) then
begin
t := System.Type.GetTypeFromHandle(ATypeHandle);
t := t.GetNestedType('@Meta' + t.name, BindingFlags(Integer(BindingFlags.Public) or
Integer(BindingFlags.NonPublic)));
if Assigned(t) then
begin
Result := _TClass(t.GetField('@Instance').GetValue(nil));
MetaTypeMap.Add(ATypeHandle, Result);
end
else
begin
Result := _TClass.Create(ATypeHandle);
end;
end;
end;
//-----------------------------------------Borland.Delphi.System.pas--
Delphi.NET使用一个哈希表MetaTypeMap来缓存Token到元类的转换关系,
因为这种转换是耗时且较频繁的操作,这主要是为优化现有Delphi代码对元类的大量使用。
对一个Token,Delphi.NET先将其转换为一个Type类型,然后取其嵌套子类,
子类名就是@Meta前缀加类名,元类允许是公开或私有。
如果找到元类的类型,则从元类的@Instance字段获取值,也就是元类的一个实例;
否则使用此Token构造一个新的元类并返回。
这样Delphi.NET就完成了TClass机制在CLR环境下的映射。