2016-03-02 51 views
3

我有一个基类TThread它有子类如TThreadSockTThreadPool,它们覆盖.Terminate()方法。而这些孩子的(如TThreadSockDownloadTThreadPoolCollect)inherite这些.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); 

我的问题是:我有一个可以容纳一切的列表,所以最常见的分母是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 ... 

...并且你会明白这会导致什么。没多少提及,我必须在其他地方使用此代码。如果我打电话oThread.Terminate()那么基类的代码被执行,这不是我想要的。并且将方法定义为virtual也不会完全起作用,因为每个儿童级别都可能是“最后一个”级别。或不。

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

我至少能够推广这个代码,所以我只需要写一次?像对象FindMethod我也有告诉它的类类型?

+3

您需要在此处添加更多信息。 '终止'不是虚拟的,所以当你说你'重写'终止'“,这是不正确的。您不能覆盖非虚拟方法。你应该显示你的'Terminate'方法的实现,它*隐藏*而不是*覆盖*。当然,正确的解决方案是调用虚拟方法。虚拟方法应该是我们不能说的。可能你应该调用'Terminate',但是重载'TermaintedSet'。但是这个问题没有足够的细节让我们说。当然,你最后的两段并不是解决方案! –

+0

我知道“覆盖”是一个关键字,但我的意思是非技术性的术语,如重新定义,或者是:“隐藏”。增强的类定义细节。 – AmigoJack

+0

你的编辑没有帮助,因为我们看不到这些'Terminate'方法。准确使用术语非常重要。 –

回答

4

解决此问题的正确方法是使用虚拟方法。此机制旨在允许基于对象的运行时类型的方法分派。换句话说,正是你的工作类型检查代码确实。

但是你正在努力处理你想要命名你的方法Terminate,这是一个非虚拟的现有方法的名称。那么,如何克服这一点。

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

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

请注意拨打TerminatedSet。这是一种虚拟方法,其实现如下:

procedure TThread.TerminatedSet; 
begin 
end; 

它什么都不做。它已被提供允许您在派生类中重写它,并在调用非虚方法Terminate时调用它。

所以,你可以这样做:

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

.... 
procedure TMyDerivedThread.TerminatedSet; 
begin 
    inherited; 
    // do your class specific tasks here 
end; 

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

oThread := GetNextThread; 
oThread.Terminate; 

现在,在另一方面,你Terminate方法不叫TThread.Terminate是合理的。在这种情况下,方法会有所不同。您仍然需要虚拟方法,但如果TThread类已经不包含合适的虚拟,则需要引入一个虚拟方法。这意味着派生一个新的基类来引入这个虚拟方法。

type 
    TBaseThread = class(TThread) 
    public 
    procedure MyTerminate; virtual; abstract; 
    end; 

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

现在你可以重写这个虚拟方法,就像任何其他的东西,我相信你已经明白了。您需要做的另一个更改是,在对线程实例进行操作时,不要保留TThread引用,而是保留TBaseThread引用。

+0

我的'TThread'的定义要早得多(Delphi 7): 'procedure TThread.Terminate; 开始 FTerminated:= True; end;' 但是你的回答我现在也理解了你的第一个评论,它给了我至少一个我可以尝试的新想法:我将创建一个支持它的TThread子,然后用它作为最常见的demoninator,这应该意味着调用'(TThreadPlus.Create).TerminateSet();'会去正确的代码,因为'TThreadPlus.TerminateSet()'总是虚拟的。 – AmigoJack

+0

虚拟方法调度无疑是关键。我认为这留下了两个选择。如果存在一个好的候选者,那么可以覆盖现有的虚拟。或者介绍你自己的。所以我相信你所需要的只是答案。 –

+0

这真的帮了我一个'.Terminate()'方法,它总是在父类(泛化)上调用,它自己调用虚拟的'.TerminatedSet()',因此总是落在孩子的最后。从来没有想过这样的组合。谢谢,大卫。 – AmigoJack