Delphi 类引用 Class Reference 元类 MetaClass 用法

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 [ 0 .. 1 ] of TObject; { 类的变量数组 }
  i : Integer; { 循环变量 }
  CR : array [ 0 .. 1 ] of CRef; { 类引用数组 }

  { TEmployee }
procedure TEmployee.Infor;
begin
  Writeln( ’ 姓名 : ’, name, ’ ; 部门名称 : ’, DeptName );
end;

begin
  CR[ 0 ] := TPerson; { 给类引用赋值 }
  CR[ 1 ] := TEmployee;
  for i := 0 to 1 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[ 0, strgrid.Selection.Top ];
  sValue := strgrid.Cells[ 1, strgrid.Selection.Top ];
  EditorClass := EditorClassList.Editors[ sField ];
  Editor := EditorClass.Create;
  Editor.Field := sField;
  Editor.Step := Step;
  Editor.Edit( sValue );
  Editor.Free;
  strgrid.Cells[ 1, 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:= -1;
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

时间: 2024-10-29 19:06:40

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

1.元类介绍/2.自定义元类控制类的行为/3.控制类的实例化行为/4.控制类的实例化行为的应用

1.元类介绍 1.储备知识exec()参数1;字符串形式得命令参数2.全局作用域(字典形式),如果不指定默认就使用globals()参数3.局部作用域(字典形式),如果不指定默认就使用locals() 2.python 一切皆对象 ,对象可以怎么用? 2.1. 都可以被引用 x=obj 2.2. 都可以当作函数得参数传入 2.3. 都可以当作函数得返回值 2.4. 都可以当作容器类得元素 li=[func,time] # 类也是对象,Foo=type() 类是属于type() 类 3.什么叫元类

Python之路(十二):描述符,类装饰器,元类

python基础之面向对象(描述符.类装饰器及元类) 描述符 描述符(__get__,__set__,__delete__)   # 这里着重描述了python的底层实现原理 1. 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议. __get__():调用一个属性时,触发 __set__():为一个属性赋值时,触发 __delete__():采用del删除属性时,触发 1 class

反射、自定义内置方法来定制类的功能、元类

一.反射 1. 定义:通过字符串来操作类或者对象属性 2. 方法:hasattr.getattr.setattr.delattr 3. 使用方法: 1 class People: 2 def __init__(self,name): 3 self.name=name 4 def put(self): 5 print('%s is putting'%self.name) 6 def get(self): 7 print('%s get sth'%self.name) 8 def run(self)

Python基础(30)——上下文管理,描述符,类装饰器,元类

上下文管理 with with open ('a.txt')  as f :       open(a.txt)就是实例化文件得到了一个对象,然后把对象赋值个f ,with 一个f的对象 ,后面不需要再写关闭,是因为类中间定义了一个协议,__enter__   __exit__来实现 原文地址:https://www.cnblogs.com/dayouge/p/11218698.html

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

译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了985点的赞同点数,更有人评论说这段回复应该加入到Python的官方文档中去.而e-satis同学本人在Stack Overflow中的声望积分也高达6

python 类和元类(metaclass)的理解和简单运用

(一) python中的类 首先这里讨论的python类,都基于继承于object的新式类进行讨论. 首先在python中,所有东西都是对象.这句话非常重要要理解元类我要重新来理解一下python中的类 class Trick(object): pass 当python在执行带class语句的时候,会初始化一个类对象放在内存里面.例如这里会初始化一个Trick对象 这个对象(类)自身拥有创建对象(通常我们说的实例,但是在python中还是对象)的能力. 为了方便后续理解,我们可以先尝试一下在新式

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

本文由 伯乐在线 - bigship 翻译 英文出处:stackoverflow 译文:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了9

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

深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例模式的那一节有些疑惑.因此花了几天时间研究下元类这个概念.通过学习元类,我对python的面向对象有了更加深入的了解.这里将一篇写的非常好的文章基本照搬过来吧,这是一篇在Stack overflow上很热的帖子,我看http://blog.jobbole.com/21351/这篇博客对其进行了翻译. 一.理解

python——深刻理解Python中的元类(metaclass)

译注:这是一篇在Stack overflow上 很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子 和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了985点的赞同点数,更有人评论说这段 回复应该加入到Python的官方文档中去.而e-satis同学本人在Stack Overflow中的声望积分也