如何将动态属性作为参数传递给函数?

时间:2017-01-19 18:33:17

标签: delphi delphi-7

假设我有一个对象,在我的情况下是一个TThread,我想在一个函数中动态检查其中一个属性,在本例中为TThread.Terminated。

是否有一种传递'属性'的简洁方法,以便我实际上传递了getter函数,并可以动态检查该属性的值?

更多细节:

我的具体问题是我第一次在Delphi中实现线程化。我不喜欢标准的Delphi Threading实现似乎是创建一个自定义线程类来执行你想要线程化的方法。我找到了一个令人满意的解决方案,其中一个使用委托来定义要执行的方法,然后在创建时将实际方法传递给线程对象,从而允许更好地分离/封装线程代码以及由线。详情如下。

然而,我发现上述想法的实现使用TThread.Suspend来允许提前终止正在执行的方法。 Delphi文档将TThread.Suspend标记为已弃用,而是建议长时间运行的方法应检查Terminated属性,以便在父线程请求时允许提前终止。因此,我需要一种方法将TThread.Terminated的引用传递给它正在执行的方法,以便该方法可以实现它自己的提前终止。

我无法使用var语法通过引用传递属性。 我不能通过引用传递底层的FTerminated字段,因为它是TThread的私有成员。 我知道我可以将TThread对象的引用传递给方法,但是这种类型的循环引用似乎是解决问题的一种黑客方式。

那么,是否存在动态传递属性的“接受”方式,还是我坚持使用当前的方法?

现行守则:     接口

uses
  Windows, Messages, Classes, Forms;

type
  // Method Thread Will Execute. Acts on Data. Runs in RunningThread.
  // (Reference to RunningThread is past so method can check runningthread.terminated
  //    to allow long running methods to terminate early when parent thread requests it)
  TThreadMethod = procedure (Data: pointer; RunningThread: TThread) of object;
  //A Simple Thread Which Excecutes a Method on Some Data
  TSimpleThread = class(TThread)
    protected
       Method: TThreadMethod;
       Data: pointer;
       procedure Execute; override;
    public
       constructor Create(Method: TThreadMethod; Data: pointer; CreateSuspended: boolean); overload;
   end;

implementation
  // SimpleThread
  constructor TSimpleThread.Create(Method: TThreadMethod;
                                Data: pointer;
                                CreateSuspended: boolean );
   begin
     Self.Method := Method;
     Self.Data := Data;
     inherited Create(CreateSuspended);
     Self.FreeOnTerminate := True;
   end;

  procedure TSimpleThread.Execute();
   begin
     Self.Method(Data, Self);
   end;

1 个答案:

答案 0 :(得分:4)

  

假设我有一个对象,在我的情况下是一个TThread,我想在一个函数中动态检查其中一个属性,在本例中为TThread.Terminated。

处理这个问题的最好方法是从TThread派生一个新类并覆盖其虚拟Execute()方法直接运行你的线程代码,然后该代码可以访问对象' s需要时Terminated属性。就像你的榜样一样。

如果您的线程代码无法直接放入Execute()(例如,您使用的是TThread.CreateAnonymousThread()TTask),那么您无法通过原始{{1}对象指针直接指向线程程序,一切都不会丢失。 TThread具有TThread类属性,用于访问运行调用代码的CurrentThread对象。当TThread开始运行时,它会将TThread指针存储在Self变量中(因此每个线程上下文都会获得自己的变量副本),thread属性随后会访问该变量。

但是,您的问题标记为Delphi 7,它不支持匿名过程,匿名线程或任务,因此您基本上无法手动将CurrentThread对象传递给您的过程代码。如果您不想将对象指针作为过程参数传递,则可以使用自己的TThread变量。

  

是否有一种干净的方式来传递'属性'这样我实际上传递了getter函数,可以动态检查该属性的值吗?

对于初学者,没有getter函数,thread属性指示返回Terminated成员,无论如何都是FTerminated。您需要实际的private对象指针才能从TThread属性中读取。

传递指向Terminated成员本身的指针/引用的唯一方法是使用Extended RTTI,这在Delphi 7中不存在。所以这对你没有帮助。 Delphi 7中的旧RTTI不够丰富,无法访问FTerminated成员。

  

然而,我发现上述想法的实现使用FTerminated来允许提前终止正在执行的方法。

然后你正在处理一个糟糕的实现。

除非您指的是TThread.Suspend构造函数的ACreateSuspended参数,否则使用起来非常安全。如果它设置为True,则不创建线程然后暂停,它将以挂起状态创建以开始。当你准备好在创建线程后启动线程时,你只需要调用TThread(或后来的Delphi版本中的Resume())。

否则,只需将Start()设置为False,并在所有构造函数退出后让它自动启动。

  

Delphi文档将TThread.Suspend标记为已弃用

这是正确的。从线程外部使用ACreateSuspended永远不会安全,因为其他线程无法确保线程处于安全状态以便挂起。 Suspend()只有在线程从自己的Suspend()方法中挂起时才可以安全使用,因为只有该线程知道何时执行挂起是安全的。但即使这样,通常有更好的方法来实现线程暂停而不实际使用Execute()(比如等待Suspend())。

  

因此,我需要一种方法将对TThread.Terminated的引用传递给它正在执行的方法,以便该方法可以实现它自己的提前终止。

不,您需要将对TEvent对象本身的引用传递给您的过程,而不是对任何特定属性的引用。

  

我无法使用var语法通过引用传递属性。我无法通过引用传递底层的FTerminated字段,因为它是TThread的私有成员。

正确。

  

我知道我可以将TThread对象的引用传递给方法,但是这种类型的循环引用似乎是解决问题的一种黑客方式。

如果您想使用TThread属性,这是唯一的方法。

否则,根本就不要使用TThread.Terminated。请使用您自己的信号机制。 TThread.Terminated属性只是Terminated标志,仅此而已。它以便利提供,而不是要求。您可以使用任何想要表示退出程序的信号以及终止自身的线程。

  

那么,有没有被接受的'动态传递属性的方法,还是我坚持使用当前的方法?

您当前的方法基本上是您必须要做的,特别是在Delphi 7中。对于后来的Delphi版本,您基本上是在重新创建Boolean所做的事情,例如:

TThread.CreateAnonymousThread()