【问题标题】:How to set a record field as 'Procedure of object' before an object exists so that it can run如何在对象存在之前将记录字段设置为“对象过程”以便它可以运行
【发布时间】:2013-03-06 17:18:43
【问题描述】:

我知道的标题很不时髦。

我有一系列文本行,需要按特定顺序执行特定操作。我通过定义以下记录结构想出了一种方法:

TProcessOrderRecord = record
  RecordTypes:       TByteSet;
  InitialiseProcedure: TPreScanProc;
  ProcessProcedure:    TProcessRecord;
  FinaliseProcedure:   TEndScanProc;
end;

AProcessOrderArray = array of TProcessOrderRecord;

Initialise 倾向于调用一个构造函数,该构造函数将填充宿主对象中的一个字段。

Process 将是对象上的一个过程,它将为每个与 RecordTypes 中的记录类型匹配的文本行调用。

Finalise 将倾向于调用析构函数,并可能在它知道已处理完整的记录集时进行任何检查。

处理这个数组的方法很简单:

procedure TImport.ScanTransferFile;
var
  i: integer;
  lArrayToProcess: AProcessOrderArray;
begin
  lArrayToProcess := SetUpProcessingOrder(NLPGApp.ImportType);
  for i := low(lArrayToProcess) to high(lArrayToProcess) do
  begin
    ProcessRecordType(lArrayToProcess[i].RecordTypes,     lArrayToProcess[i].InitialiseProcedure, lArrayToProcess[i].ProcessProcedure, lArrayToProcess[i].FinaliseProcedure);
  end;
end;

procedure TImport.ProcessRecordType(const RecordTypesToFind: TByteSet; PreScanProcedure: TPreScanProc; OnFindRecord: TProcessRecord; OnCompleteScan: TEndScanProc);
var
  lLineOfText: string;
  lIntegerRecordID: byte;
begin
  if Assigned(PreScanProcedure) then PreScanProcedure;
  try
    if assigned(OnFindRecord) then
    begin
      Reader.GoToStartOfFile;
      while not Reader.EndOfFile do
      begin
        lLineOfText := Reader.ReadLine;
        lIntegerRecordID := StrToIntDef(GetRecordID(lLineOfText), 0);
        if lIntegerRecordID in RecordTypesToFind then
        begin
          try
            OnFindRecord(lLineOfText);
          except
            on E: MyAppException do
            begin
            // either raise to exit or log and carry on
            end;
          end;
        end;
      end;
    end;
  finally
    // OnCompleteScan usually contains calls to destructors, so ensure it's called
    if Assigned(OnCompleteScan) then OnCompleteScan;
  end;
end;

我的问题是我想这样定义一个记录:

RecordTypes = [10]
InitialiseProcedure = ProcToCreateFMyObj
ProcessProcedure = FMyObj.do
FinaliseProcedure = ProcToFreeFMyObj

这编译得很好,但是当调用 ProcessProcedure 时,因为在设置 ProcessProcedure 时 FMyObj 为 nil,即使现在设置了 FMyObj,TMyObj 的实例也是 nil。有没有什么干净的方法可以让记录在调用时而不是在第一次分配时指向 FMyObj 的实例?

目前我已经在宿主对象上使用“调用者”方法,然后可以在需要时调用 FMyObj 实例,但这会创建一个包含大量单行方法的非常臃肿的对象。

编辑以澄清/复杂化问题

有时FObj 的一个实例可以处理多种类型的记录(通常如果它们具有主从关系)。在这种情况下,第一个记录类型的InitialiseProcedure将创建FObj,第二个记录的FinaliseProcedure将释放FObj,并且每个记录的ProcessProcedure可以引用FObj的不同过程(do1和@ 987654332@).

【问题讨论】:

  • FMyObj 在哪里声明?
  • FMyObj 是 TImport 类的成员。 TImport在设置的时候就存在(所以FMyObj当时是一个有效的指针,只是指向nil)。
  • 如果FMyObj在TImport类中,而ProcessRecordType是同一个类的方法,我不明白为什么需要使用方法指针,因为你可以随时直接调用 FMyObj.Do。你想完成什么?
  • 我的 AProcessOrderArray 可能包含几组方法,每组方法都使用自己的(或共享)对象。我可以使用大量的 CASE 语句来做到这一点,但是由于我有可能对 AProcessOrderArrays 进行多种配置,因此我更喜欢单独设置它并具有非常简单的调用过程的想法。我真的很想将它们声明为 const 数组,但这会造成更大的悲痛!
  • 一定要使用记录,还是可以改成使用类或接口?

标签: delphi delphi-2006


【解决方案1】:

目前我已经在宿主对象上使用“调用者”方法,然后可以在需要时调用 FMyObj 实例,但这会创建一个包含大量单行方法的非常臃肿的对象。

这是正确的解决方案。由于实例在初始化时不可用,因此您别无选择。

当你使用of object 时,你定义了一个叫做method pointer 的东西。当您分配给方法指针类型的变量时,该实例在分配点被捕获。与方法指针关联的实例没有动态解析的机制。实现这一目标的唯一方法是使用运行时委托,这是您目前正在做的。通常情况下,使用另一层间接来解决问题!

包含许多方法的记录看起来非常像interface。我怀疑最优雅的解决方案将涉及interface。也许在调用时您可以调用一个返回interface 的函数。并且该函数将在调用时使用FMyObj 的值来定位相应的接口。

【讨论】:

  • @serg 你为什么这样?从表面上看,就像针锋相对。
  • @serg 你为什么这么说?答案的哪些方面使其无效?你的断言背后有什么实质内容吗?最后一段解释了为什么不可能按照提问者的意愿去做。我需要更好地解释吗?
  • 我已经给出了答案。可以做提问者想做的事。答案不是推荐的解决方案这一点无关紧要。我在回答中提到过。
  • @serg 不,这是不可能的。你误解了什么是想要的。询问者希望方法指针在运行时调用时自动绑定到存储在 FMyObject 中的对象引用。这就是“是否有任何干净的方法可以让记录在调用时而不是在第一次分配时指向 FMyObj 的实例?”我觉得你以牙还牙的小动作令人失望。
  • 我对你的两个答案都投了赞成票:+1 表示 David 提到了 Interface,因为这是最明智的解决方案,+1 表示 serg 回答了这个问题。就像塞尔格已经提到的那样,他永远不会这样做,但这是一个有效的答案......
【解决方案2】:

是的,可以对您的记录进行额外的运行时初始化:

var
  A: TProcessOrderRecord;

begin
  ..
  TMethod(A.ProcessProcedure).Data:= FMyObj;
  ..
end;

虽然我更喜欢不同的解决方案,比如您已经使用的解决方案。

【讨论】:

  • -1 这绝不是一个好主意。如果你手头有FMyObj,那么你应该只分配整个方法指针。
  • 我同意这应该可行(并且我发现一篇网络文章在询问之前做了类似的事情),但是正如你所说,我认为另一种解决方案是首选,因为它不是 Delphi 语言的一个众所周知的方面虽然如果我是唯一一个查看代码的人就可以了,而且它是相当静态的,但情况并非如此。
  • @MattAllwood 你为什么要这么做?如果你可以这样做,你可以这样做:A.ProcessProcedure := FMyObj.ProcessProcedure.
  • @MattAllwood - 我曾经有过“拆分”方法的数据和代码部分以获得更紧凑的解决方案的想法,但最终更喜欢“臃肿”的解决方案,因为它更清晰,更易于维护 - 请参阅@ 987654321@.
  • @David - 同意反射是相同的 - 任何一种方式都使一开始就尝试设置整个结构无效
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多