我很确定可以在Delphi中动态调用类及其成员函数,但我似乎无法使其工作。我错过了什么?
// Here's a list of classes (some code removed for clarity)
moClassList : TList;
moClassList.Add( TClassA );
moClassList.Add( TClassB );
// Here is where I want to call an object's member function if the
// object's class is in the list:
for i := 0 to moClassList.Count - 1 do
if oObject is TClass(moClassList[i]) then
with oObject as TClass(moClassList[i]) do
Foo();
我在编译时获得Foo()的未声明标识符。
澄清/补充资料:
我想要完成的是在业务类之间创建一个Change Notification系统。 A类登记以通知B类的变化,并且系统存储A类 - >的映射。 B类。然后,当B类对象发生变化时,系统将调用A.Foo()来处理变更。我希望通知系统在可能的情况下不需要任何硬编码类。对于注册通知的任何类,总会有一个Foo()。
也许这不可能完成,或者我的问题有一个完全不同的更好的方法。
顺便说一句,这不是一个“观察者”设计模式,因为它不处理内存中的对象。管理相关持久数据之间的变化似乎是一个需要解决的标准问题,但我没有找到很多关于它的讨论。
再次,非常感谢任何协助。
杰夫
答案 0 :(得分:3)
首先,你使用TList
做了一些非常不寻常的事情:TList是一个UNTYPED POINTERS列表。您可以将任何指针添加到该列表中,当您执行moClassList.Add( TClassA )
时,您实际上是将类TClassA的引用添加到列表中。从技术上讲,这没有错,它只是非常不寻常:如果你真的想要添加一个类,我希望看到TClassList
!或TList<TClass>
如果你使用支持它的Delphi版本。
然后,您循环遍历列表的内容,并且您正在检查oObject
是否属于列表中的类型。所以你确实想要那个列表中的类。测试将正常工作并测试对象是否属于该类型,但是当您执行with oObject as TClass(moClassList[i]) do
时,实际上是将对象转换为... TObject
。不是你想要的,我敢肯定!
这里还有另一个问题:在该上下文中使用Foo()
可能不起作用。 TObject
不包含Foo()方法,但上下文中可能有其他Foo()方法:这是with
关键字的问题!
最后回答标题栏中的问题:Delphi不是动态语言。编译器无法在编译时调用它不知道的方法。您需要找到一种表达您想要的OOP方式(使用简单的继承或接口),或者您可以使用RTTI调用该函数。
编辑。
您的所有业务类都需要实现某种通知请求管理,因此您的设计可以从基类中获益。声明一个实现您所需要的基类,然后从中派生所有业务类:
TBusinessBase = class
public
procedure RegisterNotification(...);
procedure UnregisterNotification(...);
procedure Foo;virtual;abstract;
end;
在您的初始示例中,您不再需要受支持的类列表。你只需要这样做:
oObject.Foo;
不需要进行类型测试,因为Delphi是强类型的。无需投射,因为您可以声明oObject": TBusinessBase
。
或者,如果由于某种原因您无法更改所有对象的继承,则可以使用接口。
答案 1 :(得分:2)
TClass
已定义:
TClass = class of TObject;
然后编写oObject as TClass
,这实际上是一个空操作,因为oObject
已经是TObject
。
你需要的是这样的东西:
type
TFoo = class
procedure Foo();
end;
TFooClass = class of TFoo;
TBar = class(TFoo)
procedure Bar();
end;
....
if oObject is TFooClass(moClassList[i]) then
with oObject as TFooClass(moClassList[i]) do
Foo();
这解释了为什么你试图调用Foo()
不能编译,但我根本不知道你想要实现什么。即使在你澄清之后我也很难理解这个问题。
答案 2 :(得分:0)
这是一个非常人为的例子(使用数组而不是TList)我认为你正在尝试做的事情(错误处理和尝试......为了清楚起见,故意省略)。
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TBaseClass=class(TObject)
procedure Foo; virtual;
end;
TClassA=class(TBaseClass)
procedure Foo; override;
end;
TClassB=class(TBaseClass)
procedure Foo; override;
end;
TClassArray= array of TBaseClass;
{ TClassB }
procedure TClassB.Foo;
begin
Writeln('TClassB.Foo() called.');
end;
{ TClassA }
procedure TClassA.Foo;
begin
Writeln('TClassA.Foo() called.');
end;
var
Base: TBaseClass;
ClassArr: TClassArray;
{ TBaseClass }
procedure TBaseClass.Foo;
begin
Writeln('TBaseClass.Foo called!!!!!!!!');
end;
begin
ClassArr := TClassArray.Create(TClassA.Create, TClassB.Create);
for Base in ClassArr do
Base.Foo;
for Base in ClassArr do
Base.Free;
ReadLn;
end.