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

标签 delphi delphi-7

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

是否有一种干净的方法来传递“属性”,这样我实际上是在传递 getter 函数,因此可以动态检查该属性的值吗?

更多详情:

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

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

我无法使用 var 语法通过引用传递属性。
我不能通过引用传递底层 FTerminated 字段,因为它是 TThread 的私有(private)成员。
我知道我可以将 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;

最佳答案

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 派生一个新类。并覆盖其虚拟 Execute()方法直接运行您的线程代码,然后该代码可以访问对象的Terminated需要时的属性(property)。就像你的例子一样。

如果您的线程代码无法放入Execute()直接(例如,您使用 TThread.CreateAnonymousThread()TTask 代替),您无法通过原始 TThread对象指针直接指向线程过程,一切都不会丢失。 TThread有一个 CurrentThread访问 TThread 的类属性正在运行调用代码的对象。当TThread开始运行,它存储它的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属性指示返回 FTerminated成员,即 private反正。您需要实际的 TThread对象指针,以便从 Terminated 中读取属性(property)。

传递指向 FTerminated 的指针/引用的唯一方法成员(member)本身是使用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.



然后你正在使用一个糟糕的实现。

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

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

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属性(property)。

否则,干脆不要使用 TThread.Terminated一点也不。请改用您自己的信号机制。 Terminated属性只是 Boolean国旗,仅此而已。提供它是为了方便,而不是要求。您可以使用任何您想要的信号来指示您的过程退出并让线程自行终止。

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



您当前的方法基本上是您必须做的,尤其是在 Delphi 7 中。对于以后的 Delphi 版本,您基本上是在重新创建 TThread.CreateAnonymousThread()做,例如:
TThread.CreateAnonymousThread(
  procedure
  begin
    MyMethod(MyData, TThread.CurrentThread);
  end
).Start;

关于delphi - 如何将动态属性作为参数传递给函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41749200/

相关文章:

python - 如何使用 python ctypes 访问使用 Delphi 编码的 dll 中的指针返回的数组值?

c++ - 如何使用同步()?

delphi - Delphi 中的录音/保存

delphi - 如何绘制 Unicode 文本?

delphi - 将 Delphi 7 属性编辑器转换为 Delphi XE2

c# - 显示超过 24 小时的时间跨度的标准方法是什么?

security - Delphi和Microsoft ATL安全问题

delphi - 有人会建议重新创建新项目并重新添加源吗?为什么?

delphi - 如何获取列出的网页的所有元素Id?

Delphi 7、FastMM 和 DUnit GUITesting 因错误内存泄漏而导致测试失败