动态调用成员函数

时间:2011-03-09 22:17:50

标签: delphi

我很确定可以在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()。

也许这不可能完成,或者我的问题有一个完全不同的更好的方法。

顺便说一句,这不是一个“观察者”设计模式,因为它不处理内存中的对象。管理相关持久数据之间的变化似乎是一个需要解决的标准问题,但我没有找到很多关于它的讨论。

再次,非常感谢任何协助。

杰夫

3 个答案:

答案 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.