tsm*_*smr 1 delphi oop inheritance multithreading
我很难从基本的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;
Run Code Online (Sandbox Code Playgroud)
我在TThread的实现中看到,在AfterConstruction中自动调用Executed方法,但是如何让它指向派生类中声明的方法呢?
谢谢!
qua*_*oft 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;
Run Code Online (Sandbox Code Playgroud)
首先,忽略private该类部分中的任何内容.如果这些字段和方法被标记为private,我们不应该在后代类中使用它们.
然后,寻找任何abstract方法.抽象方法的实现留给了后代类.所以这些是您希望在代码中实现的方法.抽象方法通常使用父类中的一种方法间接调用.
在您的情况下,TThread该类只有一个抽象方法:
procedure Execute; virtual; abstract;
Run Code Online (Sandbox Code Playgroud)
文档说你需要
通过插入执行线程时应执行的代码来定义线程对象的Execute方法.
该文件听起来有点模糊,但这样做的正确方法是这是真的"覆盖"在接口中的方法,而不是"重新引入"吧:
TMyFileThread = class(TMyBaseThread)
...
protected
procedure Execute; override;
...
Run Code Online (Sandbox Code Playgroud)
然后在实现中实现它:
procedure TMyFileThread.Execute;
begin
while not Terminated do
Sleep(1); // do some other stuff
end;
Run Code Online (Sandbox Code Playgroud)
您可能会注意到我们如何Execute在该protected部分中声明了方法的覆盖定义.这是必需的,因为父类中方法的定义也在该protected部分中,因此我们只能在具有更高可见性(protected或public)的部分中覆盖它.
覆盖方法时很少需要增加可见性,因此我们只保持相同的可见性.
我们使用override关键字告诉基类使用该方法的变体而不是它自己的变体.如果你错过了override关键字,那么Execute根本不会调用该方法,并且基类会尝试调用它自己的Execute方法(如果有的话).
另外需要注意的是,您不需要Execute在基类中重新声明该方法,因为您没有在那里实现它.这就是为什么你应该删除以下定义:
TMyBaseThread = class(TThread)
...
//procedure Execute; virtual; abstract; <- remove this
...
Run Code Online (Sandbox Code Playgroud)
execute方法已在TThread类中定义.
现在,让我们看一下构造函数.基类有一个常规构造函数既不是virtual,也不是dynamic:
public
constructor Create(CreateSuspended: Boolean);
Run Code Online (Sandbox Code Playgroud)
这意味着你不能覆盖那些构造函数,如果你想在对象创建时添加额外的逻辑,你应该创建自己的构造函数来包装它们.
正确的方法是只使用一组不同的参数声明一个构造函数,而不重新引入,重载或覆盖基类:
public
//constructor Create(Param : TParam); reintroduce; virtual;
constructor Create(Param : TParam);
Run Code Online (Sandbox Code Playgroud)
此外,请记住,构造函数应该几乎总是在该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;
Run Code Online (Sandbox Code Playgroud)
该定义不需要关键字:
TMyFileThread = class(TMyBaseThread)
...
public
constructor Create(OtherParam : TOtherParam);
Run Code Online (Sandbox Code Playgroud)
您是否注意到我们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;
Run Code Online (Sandbox Code Playgroud)
PS.实际上,使用继承对TThread插件体系结构或任务工作者来说非常有用.您可以查看相关示例.