请问RTTI高手,如何在运行时判断一个类中,是否存在特定名称的一个Published字段?(100分)

  • 主题发起人 主题发起人 道明德
  • 开始时间 开始时间

道明德

Unregistered / Unconfirmed
GUEST, unregistred user!
举例如下:
AClass = Class(TPersistent)
private
FTotal :Integer;
published
TitleLabel :TLabel
//类的一个TLabel成员,在创建时赋值
property Total :integer read FTotal
//一个普通属性,用于对比TitleLabel
end;
...
我想判断AClass中是否存在指定名称的属性或字段
IsPublishedProp(AClass ,'Total')可返回True
但[red]IsPublishedProp(AClass ,'TitleLabel')却返回False[/red]

既然Delphi有专门说明:在Published段中允许对象类型的成员存在,
而Published就是为了在运行时可以访问,所以应该有办法访问到TitleLabel。

我用GetTypeData(AClass.Create.classinfo)^.PropCount查看它的属性个数,
也只能访问到1个属性, [:(]

现在还没有找到有效的方法,特向各位大富翁请教,欢迎任何提示和建议,先谢了。


 
AInstance := Aclass.create;
if AInstance.FieldAdrress('TitleLabel') <> nil then
 
参考下面代码
unit MainFrm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;

type
TMainForm = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Panel1: TPanel;
Button5: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Panel1Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
MainForm: TMainForm;

implementation
uses TypInfo;
{$R *.DFM}

procedure SetIntegerPropertyIfExists(AComp: TComponent
APropName: String;
AValue: Integer);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkInteger then
SetOrdProp(AComp, PropInfo, AValue);
end;
end;

procedure SetObjectPropertyIfExists(AComponent: TComponent
APropName: String;
AValue: TObject);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComponent.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkClass then
SetObjectProp(AComponent, PropInfo, AValue);
end;
end;

procedure SetBooleanPropertyIfExists(AComp: TComponent
APropName: String;
AValue: Boolean);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkEnumeration then
SetOrdProp(AComp, PropInfo, Integer(AValue));
end;
end;

procedure SetStringPropertyIfExists(AComp: TComponent
APropName: String;
AValue: String);
var
PropInfo: PPropInfo;
TK: TTypeKind;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
TK := PropInfo^.PropType^.Kind;
if (TK = tkString) or (TK = tkLString) or (TK = tkWString) then
SetStrProp(AComp, PropInfo, AValue);
end;
end;

procedure SetMethodPropertyIfExists(AComp: TComponent
APropName: String;
AMethod: TMethod);
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AComp.ClassInfo, APropName);
if PropInfo <> nil then
begin
if PropInfo^.PropType^.Kind = tkMethod then
SetMethodProp(AComp, PropInfo, AMethod);
end;
end;

procedure TMainForm.Button1Click(Sender: TObject);
begin
SetStringPropertyIfExists(Button1, 'Caption', 'Yahoo');
end;

procedure TMainForm.Button2Click(Sender: TObject);
begin
SetIntegerPropertyIfExists(Button2, 'Width', 50);
end;

procedure TMainForm.Button3Click(Sender: TObject);
begin
SetBooleanPropertyIfExists(Button3, 'Enabled', False);
end;

procedure TMainForm.Button4Click(Sender: TObject);
var
F: TFont;
begin
F := TFont.Create;
F.Name := 'Arial';
F.Size := 24;
F.Color := clRed;
SetObjectPropertyIfExists(Panel1, 'Font', F);
end;

procedure TMainForm.Button5Click(Sender: TObject);
begin
SetMethodPropertyIfExists(Button5, 'OnClick',
GetMethodProp(Panel1, 'OnClick'));
end;

procedure TMainForm.Panel1Click(Sender: TObject);
begin
ShowMessage(Button5.Caption);
end;


end.


 
简单,引用TypInfo单元,位于
$(DELPHI)/Source/Rtl/Common
然后
IsPublishedProp(AClass ,'TitleLabel');
 
tseug 你的附件程序无助于解决我的问题,但也谢谢了。

xeen,谢谢,但是前提是必须知道字段名。
能否再讲教一下,有没有办法将所有的字段名枚举出来?[:)]
 
呵呵,那就参考这个吧

unit MainFrm;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, DBClient, MidasCon, MConnect;

type

TMainForm = class(TForm)
pnlTop: TPanel;
pnlLeft: TPanel;
lbBaseClassInfo: TListBox;
spSplit: TSplitter;
lblBaseClassInfo: TLabel;
pnlRight: TPanel;
lblClassProperties: TLabel;
lbPropList: TListBox;
lbSampClasses: TListBox;
procedure FormCreate(Sender: TObject);
procedure lbSampClassesClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
MainForm: TMainForm;

implementation
uses TypInfo;

{$R *.DFM}

function CreateAClass(const AClassName: string): TObject;
{ This method illustrates how you can create a class from the class name. Note
that this requires that you register the class using RegisterClasses() as
show in the initialization method of this unit. }
var
C : TFormClass;
SomeObject: TObject;
begin
C := TFormClass(FindClass(AClassName));
SomeObject := C.Create(nil);
Result := SomeObject;
end;


procedure GetBaseClassInfo(AClass: TObject
AStrings: TStrings);
{ This method obtains some basic RTTI data from the given object and adds that
information to the AStrings parameter. }
var
ClassTypeInfo: PTypeInfo;
ClassTypeData: PTypeData;
EnumName: String;
begin
ClassTypeInfo := AClass.ClassInfo;
ClassTypeData := GetTypeData(ClassTypeInfo);
with AStrings do
begin
Add(Format('Class Name: %s', [ClassTypeInfo.Name]));
EnumName := GetEnumName(TypeInfo(TTypeKind), Integer(ClassTypeInfo.Kind));
Add(Format('Kind: %s', [EnumName]));
Add(Format('Size: %d', [AClass.InstanceSize]));
Add(Format('Defined in: %s.pas', [ClassTypeData.UnitName]));
Add(Format('Num Properties: %d',[ClassTypeData.PropCount]));
end;
end;

procedure GetClassAncestry(AClass: TObject
AStrings: TStrings);
{ This method retrieves the ancestry of a given object and adds the
class names of the ancestry to the AStrings parameter. }
var
AncestorClass: TClass;
begin
AncestorClass := AClass.ClassParent;
{ Iterate through the Parent classes starting with Sender's
Parent until the end of the ancestry is reached. }
AStrings.Add('Class Ancestry');
while AncestorClass <> nil do
begin
AStrings.Add(Format(' %s',[AncestorClass.ClassName]));
AncestorClass := AncestorClass.ClassParent;
end;
end;


procedure GetClassProperties(AClass: TObject
AStrings: TStrings);
{ This method retrieves the property names and types for the given object
and adds that information to the AStrings parameter. }
var
PropList: PPropList;
ClassTypeInfo: PTypeInfo;
ClassTypeData: PTypeData;
i: integer;
NumProps: Integer;
begin

ClassTypeInfo := AClass.ClassInfo;
ClassTypeData := GetTypeData(ClassTypeInfo);

if ClassTypeData.PropCount <> 0 then
begin
// allocate the memory needed to hold the references to the TPropInfo
// structures on the number of properties.
GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
try
// fill PropList with the pointer references to the TPropInfo structures
GetPropInfos(AClass.ClassInfo, PropList);
for i := 0 to ClassTypeData.PropCount - 1 do
// filter out properties that are events ( method pointer properties)
if not (PropList^.PropType^.Kind = tkMethod) then
AStrings.Add(Format('%s: %s', [PropList^.Name, PropList^.PropType^.Name]));

// Now get properties that are events (method pointer properties)
NumProps := GetPropList(AClass.ClassInfo, [tkMethod], PropList);
if NumProps <> 0 then begin
AStrings.Add('');
AStrings.Add(' EVENTS ================ ');
AStrings.Add('');
end;
// Fill the AStrings with the events.
for i := 0 to NumProps - 1 do
AStrings.Add(Format('%s: %s', [PropList^.Name, PropList^.PropType^.Name]));

finally
FreeMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
end;
end;

end;

procedure TMainForm.FormCreate(Sender: TObject);
begin
// Add some example classes to the list box.
lbSampClasses.Items.Add('TApplication');
lbSampClasses.Items.Add('TButton');
lbSampClasses.Items.Add('TForm');
lbSampClasses.Items.Add('TListBox');
lbSampClasses.Items.Add('TPaintBox');
lbSampClasses.Items.Add('TMidasConnection');
lbSampClasses.Items.Add('TFindDialog');
lbSampClasses.Items.Add('TOpenDialog');
lbSampClasses.Items.Add('TTimer');
lbSampClasses.Items.Add('TComponent');
lbSampClasses.Items.Add('TGraphicControl');
end;

procedure TMainForm.lbSampClassesClick(Sender: TObject);
var
SomeComp: TObject;
begin
lbBaseClassInfo.Items.Clear;
lbPropList.Items.Clear;

// Create an instance of the selected class.
SomeComp := CreateAClass(lbSampClasses.Items[lbSampClasses.ItemIndex]);
try
GetBaseClassInfo(SomeComp, lbBaseClassInfo.Items);
GetClassAncestry(SomeComp, lbBaseClassInfo.Items);
GetClassProperties(SomeComp, lbPropList.Items);
finally
SomeComp.Free;
end;
end;

initialization
begin
RegisterClasses([TApplication, TButton, TForm, TListBox, TPaintBox,
TMidasConnection, TFindDialog, TOpenDialog, TTimer, TComponent,
TGraphicControl]);
end;

end.
 
procedure GetPropertyList ( AnObject: TObject
List: TStrings);
var
PropertyIndex,
PropertyCount : Integer;
PropList : TPropList;
begin

PropertyCount := GetPropList ( AnObject.ClassInfo, tkAny,
@PropList) ;
for PropertyIndex := 0 to PropertyCount -1 do
begin
//List.Add ( PropList[PropertyIndex].PropType^.Name );
List.Add(PropList[PropertyIndex].Name);
end;
end;
 
ysai ,tseug 辛苦了
谢谢xeen,Perfect
 
提醒一下,xeen最后回复的GetPropertyList还是没有效果的。
这个问题可能比较难,以后再说了。若以后哪位高手想出了有效答案寄给我,我再加分。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
607
import
I
后退
顶部