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

How would one pass a dynamic property as a parameter to a function?

假设我有一个对象,在我的例子中是一个 TThread,我想在一个函数中动态地检查它的一个属性,在这个例子中 TThread.Terminated.

是否有一种干净的方法来传递 'property' 以便我实际上传递 getter 函数并因此可以动态检查 属性 的值?

更多详情:

我的具体问题是我第一次在 Delphi 中实现线程。我不喜欢标准 Delphi 线程实现似乎是创建一个自定义线程 class 来执行您想要线程化的方法。我找到了解决这个烦恼的令人满意的解决方案,其中使用委托来定义要执行的方法,然后在创建时将实际方法传递给线程对象,从而允许更好的 separation/encapsulation 线程代码和要执行的方法通过线程。详情如下。

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

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

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

当前代码: 界面

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;

Say I have an object, in my case a TThread, and I want to check one of it's properties dynamically in a function, in this case TThread.Terminated.

最好的处理方法是从 TThread 派生一个新的 class 并将其虚拟 Execute() 方法直接覆盖到 运行 你的线程代码,然后该代码可以在需要时访问对象的 Terminated 属性。正如您的示例所做的那样。

如果你的线程代码不能直接放在Execute()中(比如,你正在使用TThread.CreateAnonymousThread()TTask),你不能通过原始TThread 直接指向线程程序的对象指针,一切都不会丢失。 TThread 有一个 CurrentThread class 属性 来访问正在 运行 调用代码的 TThread 对象。当 TThread 启动 运行ning 时,它将其 Self 指针存储在 thread 变量中(因此每个线程上下文都有自己的变量副本),CurrentThread 属性然后访问。

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

Is there a clean way to pass the 'property' such that I am actually passing the getter function and can therefor check the value of that property dynamically?

首先,没有getter功能,Terminated 属性直接returns FTerminated成员,即private反正。您需要实际的 TThread 对象指针才能从 Terminated 属性.

中读取

将 pointer/reference 传递给 FTerminated 成员本身的唯一方法是使用 Extended RTTI,它在 Delphi 7 中不存在。帮不了你。 Delphi 7 中的旧 RTTI 不够丰富,无法访问 FTerminated 成员。

However the implementation of the above idea I found uses TThread.Suspend to allow early termination of the method being executed.

那么你正在使用一个糟糕的实现。

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

否则,只需将ACreateSuspended设置为False,并在所有构造函数退出后让它auto-start自己。

The Delphi docs mark TThread.Suspend as deprecated

没错。 Suspend() 从线程外部使用永远不安全,因为其他线程无法确保线程处于安全状态以被挂起。 Suspend() 仅当线程从其自身的 Execute() 方法中挂起时才可安全使用,因为只有该线程才知道何时可以安全地执行挂起。但即便如此,通常有更好的方法来实现线程挂起而无需实际使用 Suspend()(比如等待 TEvent)。

Thus I need a way to pass a reference to TThread.Terminated to the method it is executing so that that method can implement it's own early termination.

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

的引用

I can't pass the property by reference using the var syntax. I can't pass the underlying FTerminated field by reference, as it is a private member of TThread.

正确。

I know I can just pass a reference to the TThread object into the method, but that type of circular referencing seems like a hacky way of getting around the problem.

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

否则,根本就不要使用TThread.Terminated。请改用您自己的信号机制。 Terminated 属性 只是一个 Boolean 标志,仅此而已。它是作为方便而不是要求提供的。您可以使用任何您想要的信号来指示您的过程退出和线程自行终止。

So, is there a 'accepted' way to dynamically pass a property, or am I stuck with my current method?

你现在的方法基本上就是你必须要做的,尤其是在Delphi7.以后的Delphi版本,你基本上是re-creatingTThread.CreateAnonymousThread()做什么,例如:

TThread.CreateAnonymousThread(
  procedure
  begin
    MyMethod(MyData, TThread.CurrentThread);
  end
).Start;