delphi中类引用的使用实例

类引用
类引用(Class Reference)是一种数据类型,有时又称为元类(MetaClass),是类的类型的引用。类引用的定义形式如下:

 class of type

例如:

type
SomeClass = class of TObject; var
AnyObj: SomeClass;
TObject = class
end; TClass = class of TObject;

下面的例子说明了类引用的用法:

program Project1;
{$APPTYPE CONSOLE} type
TPerson = class { 人员类 }
Name : string; { 姓名 }
end; TEmployee = class( TPerson ) { 职员类 }
DeptName : string; { 部门名称 }
procedure Infor; { 显示职员信息 }
end; CRef = class of TObject; { 定义了一个"类引用"数据类型 } var
Employee : array [ .. ] of TObject; { 类的变量数组 }
i : Integer; { 循环变量 }
CR : array [ .. ] of CRef; { 类引用数组 } { TEmployee }
procedure TEmployee.Infor;
begin
Writeln( ’ 姓名 : ’, name, ’ ; 部门名称 : ’, DeptName );
end; begin
CR[ ] := TPerson; { 给类引用赋值 }
CR[ ] := TEmployee;
for i := to do
begin
Employee[ i ] := CR[ i ].Create; { 创建对象 }
if Employee[ i ] is TEmployee then { 判断对象的类型 }
begin
( Employee[ i ] as TEmployee ).Name := ’ 残月 ’;
( Employee[ i ] as TEmployee ).DeptName := ’ 人事部 ’;
( Employee[ i ] as TEmployee ).Infor;
end;
end;
Readln; end.

运行结果如下:

姓名:残月;部门名称:人事部

注意:上面定义了一个类引用类型的数组,其中的两个元素的数值分别为不同的两个类的类型。

代码示例:你是真的对Delphi很了解么?

procedure StepEditor( strgrid : TStringGrid; Step : TStep );
var
sValue, sField : string;
EditorClass : TStepEditorClass;
Editor : TStepEditor;
begin
sField := strgrid.Cells[ , strgrid.Selection.Top ];
sValue := strgrid.Cells[ , strgrid.Selection.Top ];
EditorClass := EditorClassList.Editors[ sField ];
Editor := EditorClass.Create;
Editor.Field := sField;
Editor.Step := Step;
Editor.Edit( sValue );
Editor.Free;
strgrid.Cells[ , strgrid.Selection.Top ] := sValue;
end;

EditorClass 是一个Class of Class, 也就是类的类 比如   TFormClass = Class of TForm;

但是不同于:TFormClass = Class( TForm ); 这是两个概念!

而 EditorClassList 里面存放的就是 类的类的列表;

Editor := EditorClass.Create;

Create是类方法,而不是对象方法,所以可以由 EditorClass来创建EditorClass的一个实例

补充:
  
  TStepEditor = Class( TObject )
  ...
  End;

TStepEditorClass = Class of TStepEditor;

基本概念

元类(meta class),也叫类引用类型(class-reference type),

可以看成是一种类的类型,以该类型声明的变量的值代表一个类。比如:

type

TClass = Class of TObject;

这样就声明了一个元类的类型。然后可以有这样的变量声明:

Var
AClass: TClass;

那么,就可以有这样的用法:

AClass := TObject;

或者:
AClass := TButton;

或者:
AClass := TForm;

等等。
因为TClass是一个TObject类型的元类,而TButton,TForm等都是自TObject派生而来,

因而TButton和TForm这样的值对于AClass都是可接受的。

A class-reference type, sometimes called a metaclass, is denoted by a construction of the form

class of type

where type is any class type.

The identifier type itself denotes a value whose type is class of type.

If type1 is an ancestor of type2, then class of type2 is assignment-compatible with class of type1. Thus

type TClass = class of TObject;
var
AnyObj: TClass;

declares a variable called AnyObj that can hold a reference to any class.

(The definition of a class-reference type cannot occur directly in a variable declaration or parameter list.)

You can assign the value nil to a variable of any class-reference type.

To see how class-reference types are used, look at the declaration of the constructor for TCollection (in the Classes unit):

type TCollectionItemClass = class of TCollectionItem;
...
constructor Create(ItemClass: TCollectionItemClass);

This declaration says that to create a TCollection instance object,

you must pass to the constructor the name of a class descending from TCollectionItem.

Class-reference types are useful when you want to invoke a class method

or virtual constructor on a class or object whose actual type is unknown at compile Time .

元类 就是类之类, 如果说 对象(引用) 是类的变量,那么 元类变量 就是 类的 类型的 变量. 
TForm = class(TCustomForm) 
TFormClass= class of TForm

{ 定义部分 }
interface type
TMyClass = class( TObject )
end; TMyClassClass = class of TMyClass; TMyClass1 = class( TMyClass )
end;
  TMyClass2 = class( TMyClass )  
end; { 执行部分 }
implementation procedure MyProcedure;
var
MyClass : TMyClassClass;
MyObj : TMyClass;
begin
{ 创建TMyClass1的实例 }
MyClass := TMyClass1;
MyObj := MyClass.Create;
MyObj.Free; { 创建TMyClass2的实例 }
MyClass := TMyClass2;
MyObj := MyClass.Create;
MyObj.Free;
end;

http://blog.csdn.net/blue_morning/article/details/8815609

这个概念本来在一个关于Delphi RTTI 介绍的文档中已经说得很清楚了。

但没有任何关于实际使用的介绍,在我明白了这个概念和如何使用后决定写一个使用说明以方便大家使用。

类的类在什么时候使用:

知道父类但需要创建具体的子类时(你不知道子类会是什么)

例如:

一个Delphi Exe程序中项目文件的Application.CreateForm,跟踪下源代码就能明白,

Delphi实现了在根本不知道我们会从TForm派生出什么类的情况下,实现了对这个类的创建。

 TComponentClass = class of TComponent;
procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
begin
Instance :=
TComponent(InstanceClass.NewInstance);
Instance.Create(Self);

...
end;

关键的代码就是加粗的这两句和类的类声明

本质:

类的类在声明时,说明相应的类及子类会被编译器附加额外的信息(RTTI),

以让系统可以找到具体子类的Create和NewInstance地址。应该就是这样。

代价:

额外的RTTI信息会使我们的类占用额外的内存,这是便利的代价。

简单的问题复杂的说明

本来问题已经说明,但还是存在一个问题:

我们的代码中什么地方需要使用class of ?

我发现这个问题说明起来很复杂,我举个我人个开发使用的例子。

在做数据库程序开发时:

我先定义一个TTableSet对象,其功能类似DataModule。

用于放置TExportTable,TExportTable类其功能类似TDataSet。

我定义了它的增、删、改、查等基本操作。

TTableSet对象有一个Add方法,大概代码如下:

procedure TTableSet.Add(const AExoprtObjectInfo: record)
var
ExprotTable: TExportTable;
begin
ExprotTable := TExportTable.Create(nil)
{ 根据AExoprtObjectInfo的数据内容具体化ExportTable对象以方便复用代码 }
end;

然后,在具体的业务功能(例如入库单管理)中需要从TExportTable继承一个入库单类

TInStorageBill = class(TExportTable)
{ 一些具体的类属性和方法 }
{ 覆盖TExportTable的Create方法以创建相应的资源 }
end;

废话了那么多,问题才终于出现了:“我怎么才能在TTableSet.Add()方法中创建TInStorageBill对象?”

或换而言之:“我怎么在在知道父类的情况下创建其不确定的子类?”。 而你们都知道答案了。

http://bbs.csdn.net/topics/90321

关于物件参考(Object reference)与类别参考(Class reference):

 type
TMyClass = class of TForm; { 宣告了关于TForm的一个类别参考,意即C++中的别名。} var
MyClass: TForm; { 宣告了关于TForm的一个物件参考。}

注意点:
  建构函数呼叫物件参考时将初始化一个物件的所有栏位,并返回一个指向此物件的指针。

建构函数呼叫类别参考时并不初始化任何栏位,只简单的返回一个指向此物件的指针。

Delphi Class of 类引用也就是类的类型,也可说是指向类的指针

Type
TControlCls = Class of TControl; function CreateComponent(ControlCls: TControlCls): TControl;
begin
result:=ControlCls.Create(Form1);
...
end; function CreateComponent(ControlCls: TControl): TControl;
begin
result:=ControlCls.Create(Form1);
...
end;

前者要求传入一个 类, 而后者要求传入一个 对象(类的实例)

type
MyClassRef = calss of TMyClass; { 表示 MyClassRef 为指向 TMyClass 或 其父类 的指针 }

类的引用 就像指向 类的指针 一样

类的引用 就是 类 的类型,可以声明一个类的引用变量, 赋给它一个类,可以通过这个 类的引用变量 创建 对象 的实例。

类之类

当你不确定调用的类模型时候用到类之类。也可以说是类指针~

How can I create an Delphi object from a class reference and ensure constructor execution?

How can I create an instance of an object using a class reference,

and ensure that the constructor is executed?

In this code example, the constructor of TMyClass will not be called:

type
TMyClass = class(TObject)
MyStrings: TStrings;
constructor Create; virtual;
end; constructor TMyClass.Create;
begin
MyStrings := TStringList.Create;
end; procedure Test;
var
Clazz: TClass;
Instance: TObject;
begin
Clazz := TMyClass;
Instance := Clazz.Create;
end;

Your code slightly modified:

type
TMyObject = class(TObject)
MyStrings: TStrings;
constructor Create; virtual;
end;
TMyClass = class of TMyObject; constructor TMyObject.Create;
begin
inherited Create;
MyStrings := TStringList.Create;
end; procedure Test;
var
C: TMyClass;
Instance: TObject;
begin
C := TMyObject;
Instance := C.Create;
end;

Use this:

type
TMyClass = class(TObject)
MyStrings: TStrings;
constructor Create; virtual;
end;
TMyClassRef = class of TMyClass; constructor TMyClass.Create;
begin
MyStrings := TStringList.Create;
end; procedure Test;
var
MyClassRef: TMyClassRef;
Instance: TObject;
begin
MyClassRef := TMyClass; { you can use TMyClass or any of its child classes. }
Instance := MyClassRef.Create; { virtual constructor will be used }
end;

Alternatively, you can use a type-casts to TMyClass (instead of class of TMyClass).

Alexander's solution is a fine one but does not suffice in certain situations.

Suppose you wish to set up a TClassFactory class where TClass references

can be stored during runtime and an arbitrary number of instances retrieved later on.

Such a class factory would never know anything about the actual types of the classes it holds

and thus cannot cast them into their according meta classes.

To invoke the correct constructors in such cases, the following approach will work.

First, we need a simple demo class (don't mind the public fields, it's just for demonstration purposes).

interface

uses
RTTI; type
THuman = class(TObject)
public
Name: string;
Age: Integer; constructor Create(); virtual;
end; implementation constructor THuman.Create();
begin
Name:= 'John Doe';
Age:= -;
end;

Now we instantiate an object of type THuman purely by RTTI and with the correct constructor call.

procedure CreateInstance();
var
someclass: TClass;
c: TRttiContext;
t: TRttiType;
v: TValue;
human1, human2, human3: THuman;
begin
someclass:= THuman; { Invoke RTTI }
c:= TRttiContext.Create;
t:= c.GetType(someclass); { Variant 1a - instantiates a THuman object but calls constructor of TObject }
human1:= t.AsInstance.MetaclassType.Create; { Variant 1b - same result as 1a }
human2:= THuman(someclass.Create); { Variant 2 - works fine }
v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]);
human3:= THuman(v.AsObject); { free RttiContext record (see text below) and the rest }
c.Free; human1.Destroy;
human2.Destroy;
human3.Destroy;
end;

You will find that the objects "human1" and "human2" have been initialized to zero,

i.e., Name='' and Age=0, which is not what we want.

The object human3 instead holds the default values provided in the constructor of THuman.

Note, however, that this method requires your classes to have constructor methods with not parameters.

All the above was not conceived by me but explained brillantly and in much more detail (e.g., the c.Free part)

in Rob Love's Tech Corner.

vcv

Delphi 类引用 Class Reference 元类 MetaClass 用法的更多相关文章

  1. 类装饰器,元类,垃圾回收GC,内建属性、内建方法,集合,functools模块,常见模块

    '''''''''类装饰器'''class Test(): def __init__(self,func): print('---初始化---') print('func name is %s'%fu ...

  2. Python属性、方法和类管理系列之----元类

    元类的介绍 请看位于下面网址的一篇文章,写的相当好. http://blog.jobbole.com/21351/ 实例补充 class Meta(type): def __new__(meta, c ...

  3. python day 11: 类的补充,元类,魔法方法,异常处理

    目录 python day 11 1. 类的补充 1.1 通过反射来查找类,创建对象,设置对象的属性与方法 1.2 类的魔法方法:getitem,setitem 1.3 元类__metaclass__ ...

  4. Python元类实战,通过元类实现数据库ORM框架

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第19篇文章,我们一起来用元类实现一个简易的ORM数据库框架. 本文主要是受到了廖雪峰老师Python3入门教程的启 ...

  5. 深刻理解Python中的元类metaclass(转)

    本文由 伯乐在线 - bigship 翻译 英文出处:stackoverflow 译文:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上很热 ...

  6. 深刻理解Python中的元类(metaclass)

    译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...

  7. [转] 深刻理解Python中的元类(metaclass)

    非常详细的一篇深入讲解Python中metaclass的文章,感谢伯乐在线-bigship翻译及作者,转载收藏. 本文由 伯乐在线 - bigship 翻译.未经许可,禁止转载!英文出处:stacko ...

  8. python 面向对象进阶之元类metaclass

    一:知识储备 exec exec:三个参数 参数一:字符串形式的命令 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为local ...

  9. [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式

    使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...

随机推荐

  1. Linux 入门记录:三、Linux 文件基本操作管理

    一.复制文件.目录 使用 cp 命令复制文件或目录: $ cp 源文件(夹)目标文件(夹) 常用参数: -r 递归复制整个目录树 -v 显示复制过程的详细信息 二.移动.重命名文件或目录 通过 mv  ...

  2. php文件读取的问题

    PHP字符编码问题 首先说下字符编码问题,当我们给定路径后如果路径中包含中文,可能会出现问题,打印到屏幕则显示没问题, 但是读取文件会报错:readfile(E:/素玄文件/app历史版本/素玄ERP ...

  3. IE8下面的line-height的bug

    当line-height小于正常值时,超出的部分将被剪裁掉

  4. 阻止父类的create,是无法阻止的

  5. oracle11g如何创建数据库

    oracle11g创建数据库的步骤如下:1.按住键盘上Windows键,打开开始菜单,找到Database Configuration Assitant并打开:2.打开数据库配置助手Database ...

  6. QT中循环显示图片和简单的显示图片

    请关注我的github https://github.com/linqiaozhou 以下实例代码不久后将会上传到我的github 这是我最近一个项目中的部分代码 //以下是简单的在QT中显示图片的代 ...

  7. 寻找与网页内容相关的图片(二)reddit的做法

    正如前文所述,内容聚合网站,比如新浪微博.推特.facebook等网站对于网页的缩略图是刚需.为了让分享的内容引人入胜,网页的图片缩略图是必不可少的.年轻人的聚集地.社交新闻网站reddit也是一个这 ...

  8. 【剑指offer】面试题 64. 求 1+2+3+...+n

    面试题 64. 求 1+2+3+...+n 题目:求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 1.采 ...

  9. 开源游戏地图编辑器MarbleMap

    开源游戏地图编辑器MarbleMap MIT协议,MarbleMap是一款as3开发的游戏地图编辑器,他免费开源,同时支持Cocos2d-x坐标系和AS3坐标系,功能丰富,不过是一款新推出的开源项目, ...

  10. linux 把用户加入一个组&从这个组中移除

    # usermod -a -G www zhou // zhou这个用户现在属于两个组 zhou www # groups zhou zhou : zhou www # gpasswd -d zhou ...