调用子类非虚方法或设置子类属性

Ami*_*ack 3 delphi polymorphism

我有一个基类TThread,它有像TThreadSockandTThreadPool这样的子类,它覆盖了该.Terminate()方法。这些孩子的孩子(如TThreadSockDownloadTThreadPoolCollect)继承了这些.Terminate()方法(甚至可能覆盖它们):

type
  TThreadSock= class( TThread )
    procedure Terminate;  // Hides TThread.Terminate
  end;
  TThreadSockDownload= class( TThreadSock );
  TThreadSockUpload= class( TThreadSock )
    procedure Terminate;  // Hides TThreadSock.Terminate
  end;

  TThreadPool= class( TThread )
    procedure Terminate;  // Hides TThread.Terminate
  end;
  TThreadPoolCollect= class( TThreadPool );
Run Code Online (Sandbox Code Playgroud)

我的问题是:我有一个可以包含所有内容的列表,所以最常见的分母是TThread. 从那个基类我需要调用最“幼稚”的.Terminate()方法。目前我的方法是这样的:

var
  oThread: TThread;
begin
  oThread:= GetNextThread();

  if oThread is TThreadSockDownload then TThreadSockDownload(oThread).Terminate() else
  if oThread is TThreadSockUpload then TThreadSockUpload(oThread).Terminate() else
  if oThread is TThreadPoolCollect then TThreadPoolCollect(oThread).Terminate() else ...
Run Code Online (Sandbox Code Playgroud)

......你就会知道这会导致什么。没什么好说的,我也必须在其他地方使用这段代码。如果我调用,oThread.Terminate()则执行基类的代码,这不是我想要的。并且将方法定义为virtual也不会完全起作用,因为每个子级别都可能是“最后一个”级别。或不。

我的最终目标是尽可能地概括这一点,所以我不需要要求每个班级都作为候选人。也许我失去了一些东西基本在这里,像GetRealClass( oThread ).Call( 'Terminate' );GetRealClass( oThread ).Set( 'Stop', TRUE );将是一个梦想。

我至少能够概括这段代码,所以我只需要编写一次吗?像FindMethod在一个对象上我也有告诉它的类类型的东西?

Dav*_*nan 5

处理这个问题的正确方法是使用虚方法。此机制旨在允许基于对象的运行时类型进行方法调度。换句话说,正是您辛苦的类型检查代码所做的。

但是您正在努力解决您想要命名您的方法的事实Terminate,这是一个非虚拟的现有方法的名称。那么,如何度过难关。

好吧,如果您决定名称Terminate是因为您的方法调用TThread.Terminate,然后执行其他任务,那么该框架为您提供了一个简单的方法。我们来看看 的实现TThread.Terminate

procedure TThread.Terminate;
begin
  if FExternalThread then
    raise EThread.CreateRes(@SThreadExternalTerminate);
  FTerminated := True;
  TerminatedSet;
end;
Run Code Online (Sandbox Code Playgroud)

请注意对 的调用TerminatedSet。那是一个虚方法,它的实现是这样的:

procedure TThread.TerminatedSet;
begin
end;
Run Code Online (Sandbox Code Playgroud)

它什么都不做。提供它是为了允许您在派生类中覆盖它,并在调用非虚拟方法时Terminate调用它。

所以你会这样做:

type
  TMyDerivedThread = class(TThread)
  protected
    procedure TerminatedSet; override;
  end;

....
procedure TMyDerivedThread.TerminatedSet; 
begin
  inherited;
  // do your class specific tasks here
end;
Run Code Online (Sandbox Code Playgroud)

然后控制线程的代码可以调用非虚Terminate方法,但仍然调用这个虚方法。

oThread := GetNextThread;
oThread.Terminate;
Run Code Online (Sandbox Code Playgroud)

现在,另一方面,您的Terminate方法可能不会调用TThread.Terminate. 在这种情况下,方法会有所不同。您仍然需要一个虚方法,但如果TThread该类不包含适当的虚方法,则需要引入一个。这意味着派生一个新的基类以引入该虚方法。

type
  TBaseThread = class(TThread)
  public
    procedure MyTerminate; virtual; abstract;
  end;
Run Code Online (Sandbox Code Playgroud)

我已经做了这个摘要,但你可能不想。我们无法判断,因为我们不知道您的线程实现是做什么的。你可以决定这个方法是否应该是抽象的。

现在你可以像其他任何方法一样覆盖这个虚拟方法,我相信你已经理解了。您需要进行的另一个更改是,TThread在对线程实例进行操作时,您不持有TBaseThread引用,而是持有引用。