我很难从基本的TThread类实现多层继承 基于我对OOP的了解应该是可能的,但也许它不能应用于线程 我的目标是使用TMyBaseThread来实现后代类通用的所有代码。这就是我的尝试:
TMyBaseThread = class(TThread)
private
procedure BuildBaseObjects(aParam : TParam);
procedure Execute; virtual; abstract;
protected
constructor Create(Param : TParam); reintroduce; virtual;
end;
TMyFileThread = class(TMyBaseThread)
private
procedure Execute; reintroduce;
public
constructor Create(OtherParam : TOtherParam); reintroduce; overload;
end;
TMyDBThread = class(TMyBaseThread)
private
procedure Execute; reintroduce;
public
constructor Create(aDB : TDatabase); reintroduce; overload;
end;
implementation
constructor TMyBaseThread.Create(Param : TParam);
begin
inherited Create(False);
Self.BuildBaseObjects(Param);
[do some stuff]
end;
constructor TMyFileThread.Create(OtherParam : TOtherParam);
var
param : TParam;
begin
inherited Create(param);
[do some other stuff]
end;
procedure TMyFileThread.Execute;
begin
while not Terminated do
doWork(); <-- this is never called
end;
constructor TMyDBThread.Create(aDB : TDatabase);
var
param : TParam;
begin
inherited Create(param);
end;
procedure TMyDBThread.Execute;
begin
while not Terminated do
doDatabaseWork(); <-- this is never called
end;
我在TThread的实现中看到,在 AfterConstruction 中会自动调用Executed方法,但是如何让它指向派生类中声明的方法呢?
谢谢!
答案 0 :(得分:12)
首先,我不能支持更多关于使用合成而不是继承来实现常用功能的Craig评论。
尽管架构选择存在疑问,但您可以从您的示例中学到很多东西。
在继承类之前,您应该研究要继承的父类的接口。为此,您可以在源代码的界面部分查找类定义,也可以查找相关文档 - System.Classes.TThread
。
您似乎已经阅读了文档,所以让我们看一下除TThread
的类定义之外的其他内容:
TThread = class
private
...
protected
procedure CheckThreadError(ErrCode: Integer); overload;
procedure CheckThreadError(Success: Boolean); overload;
procedure DoTerminate; virtual;
procedure Execute; virtual; abstract;
procedure Queue(AMethod: TThreadMethod); overload;
procedure Synchronize(AMethod: TThreadMethod); overload;
property ReturnValue: Integer read FReturnValue write FReturnValue;
property Terminated: Boolean read FTerminated;
public
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
procedure AfterConstruction; override;
procedure Resume;
procedure Suspend;
procedure Terminate;
function WaitFor: LongWord;
class procedure Queue(AThread: TThread; AMethod: TThreadMethod); overload;
class procedure RemoveQueuedEvents(AThread: TThread; AMethod: TThreadMethod);
class procedure StaticQueue(AThread: TThread; AMethod: TThreadMethod);
class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod); overload;
class procedure StaticSynchronize(AThread: TThread; AMethod: TThreadMethod);
property FatalException: TObject read FFatalException;
property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;
property Handle: THandle read FHandle;
property Priority: TThreadPriority read GetPriority write SetPriority;
property Suspended: Boolean read FSuspended write SetSuspended;
property ThreadID: THandle read FThreadID;
property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;
end;
首先,忽略课程private
部分中的任何内容。如果这些字段和方法被标记为private
,我们就不应该在后代类中使用它们。
然后,查找任何abstract
方法。抽象方法的实现留给了后代类。所以这些是您希望在代码中实现的方法。抽象方法通常使用父类中的一种方法称为间接。
在您的情况下,TThread
类只有一个抽象方法:
procedure Execute; virtual; abstract;
文档说你需要
通过插入代码来定义线程对象的Execute方法 应该在执行线程时执行。
确实文档听起来有点模糊,但正确的方法是&#34;覆盖&#34; 界面中的方法,而不是&#34;再引入&#34;它:
TMyFileThread = class(TMyBaseThread)
...
protected
procedure Execute; override;
...
然后在实现中实现它:
procedure TMyFileThread.Execute;
begin
while not Terminated do
Sleep(1); // do some other stuff
end;
您可能会注意到我们如何在Execute
部分中声明了protected
方法的覆盖定义。这是必需的,因为父类中的方法定义也在protected
部分中,因此我们只能在具有更高可见性(protected
或public
)的部分中覆盖它。
在覆盖方法时,您很少需要提高可见性,因此我们只保持相同的可见性。
我们使用override
关键字告诉基类使用此方法的变体而不是它自己的变体。如果您错过override
关键字,则根本不会调用Execute
方法,并且基类会尝试调用它自己的Execute方法(如果有的话)。
需要注意的另一点是,您不需要在基类中重新声明Execute
方法,因为您没有在那里实现它。这就是为什么你应该删除以下定义:
TMyBaseThread = class(TThread)
...
//procedure Execute; virtual; abstract; <- remove this
...
执行方法已在TThread
类中定义。
现在,让我们看看构造函数。基类有一个常规构造函数,既不是virtual
,也不是dynamic
:
public
constructor Create(CreateSuspended: Boolean);
这意味着你不能覆盖那些构造函数,如果你想在对象创建时添加额外的逻辑,你应该创建自己的构造函数来包装它们。
执行此操作的正确方法是仅使用不同的参数集声明构造函数,而无需重新引入,重载或覆盖基本参数:
public
//constructor Create(Param : TParam); reintroduce; virtual;
constructor Create(Param : TParam);
另外,请记住,构造函数应该几乎总是位于public
部分。
您也不需要制作构造函数virtual
。如果您的TMyFileThread
和TMyDBThread
类需要在构造函数中添加一些逻辑,而不用更改构造函数参数,则可以这样做。
当您更改参数集时,只需将继承的构造函数作为新内容中的第一个内容调用:
constructor TMyFileThread.Create(OtherParam : TOtherParam);
var
param : TParam;
begin
inherited Create(param); // It is enough to call the base constructor at the top
// do some other stuff
end;
该定义不需要关键字:
TMyFileThread = class(TMyBaseThread)
...
public
constructor Create(OtherParam : TOtherParam);
您是否注意到我们如何使用inherited Create(param)
来调用基本构造函数,但我们没有使用inherited Execute;
?这是因为Execute
方法被标记为abstract
并且在基类中没有默认实现。在抽象方法上使用inherited会导致异常,因为没有默认方法可以调用。
作为一般规则,如果您调用的基本构造函数标记为inherited Create
,则调用virtual
是必须,但即使未标记,也几乎总是需要如此。
最后,我想制定相关关键字及其最常见用途的摘要:
virtual
相同。可以节省一些内存资源,但前提是该方法未在所有后代类中重新实现,并且有许多对象是从这些类创建的。很少使用。inherited
关键字来调用基本方法。virtual
,override
关键字无关。虽然有时你可以结合两种效果。话虽如此,这是我对你的代码的解释:
interface
uses
Classes, SysUtils;
type
TParam = class
end;
TOtherParam = class
end;
TDatabase = class
end;
TMyBaseThread = class(TThread)
private
procedure BuildBaseObjects(aParam : TParam);
protected
public
constructor Create(Param : TParam);
end;
TMyFileThread = class(TMyBaseThread)
private
protected
procedure Execute; override;
public
constructor Create(OtherParam : TOtherParam);
end;
TMyDBThread = class(TMyBaseThread)
private
protected
procedure Execute; override;
public
constructor Create(aDB : TDatabase);
end;
implementation
{ TMyBaseThread }
constructor TMyBaseThread.Create(Param : TParam);
begin
inherited Create(False);
Self.BuildBaseObjects(Param);
// Do some stuff
end;
procedure TMyBaseThread.BuildBaseObjects(aParam : TParam);
begin
// Do some stuff
end;
{ TMyFileThread }
constructor TMyFileThread.Create(OtherParam : TOtherParam);
var
param : TParam;
begin
inherited Create(param); // Remember to initialize param somehow
// Do some other stuff
end;
procedure TMyFileThread.Execute;
begin
while not Terminated do
Sleep(1);
end;
{ TMyDBThread }
constructor TMyDBThread.Create(aDB : TDatabase);
var
param : TParam;
begin
inherited Create(param); // Remember to initialize param somehow
end;
procedure TMyDBThread.Execute;
begin
while not Terminated do
Sleep(1);
end;
PS。实际上在TThread
上使用继承对于插件体系结构或任务工作者来说非常有用。您可以查看相关示例。