2

私が知っている非常にきびきびとしたタイトル。

特定の操作を特定の順序で実行する必要がある一連のテキスト行があります。次のレコード構造を定義することで、これを行う方法を考え出しました。

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

AProcessOrderArray = array of TProcessOrderRecord;

Initialiseは、ホストオブジェクトのフィールドに入力するコンストラクターを呼び出す傾向があります。

プロセスは、RecordTypesのレコードタイプの1つに一致するテキスト行ごとに呼び出されるオブジェクトのプロシージャになります。

Finalizeは、デストラクタを呼び出す傾向があり、レコードの完全なセットが処理されたことを認識したときに、チェックを実行する可能性があります。

この配列を処理する手段は非常に簡単です。

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のインスタンスを指すようにレコードを取得するクリーンな方法はありますか?

現在、ホストオブジェクトに「caller」メソッドを使用して、必要に応じてFMyObjインスタンスを呼び出すことができますが、これにより、多数の単一行メソッドを含む非常に肥大化したオブジェクトが作成されます。

問題を明確化/複雑化するために編集する

の1つのインスタンスがFObj、複数のタイプのレコードを処理できる場合があります(通常、マスターと詳細の関係がある場合)。この場合、InitialiseProcedure最初のレコードタイプのが作成さFObjれ、FinaliseProcedure2番目のレコードのが解放さFObjれ、各レコードは(および)ProcessProcedureの異なるプロシージャを参照できます。FObjdo1do2

4

2 に答える 2

3

現在、ホストオブジェクトに「caller」メソッドを使用して、必要に応じてFMyObjインスタンスを呼び出すことができますが、これにより、多数の単一行メソッドを含む非常に肥大化したオブジェクトが作成されます。

それが正しい解決策です。インスタンスは初期化の時点では利用できないため、代替手段はありません。

使用するときは、メソッドポインタof objectと呼ばれるものを定義しています。メソッドポインタ型の変数に割り当てると、インスタンスは割り当ての時点でキャプチャされます。メソッドポインタに関連付けられたインスタンスを動的に解決するメカニズムはありません。これを実現する唯一の方法は、実行時の委任を使用することです。これは、現在実行していることです。よくあることですが、問題を解決するために別の間接層が使用されます。

いくつかのメソッドを含むレコードは、ひどく。のように見えますinterface。最も洗練された解決策には、が含まれると思いinterfaceます。おそらく、呼び出しの時点で、を返す関数を呼び出すことができますinterface。そして、その関数はFMyObj、呼び出し時の値を使用して適切なインターフェースを見つけます。

于 2013-03-06T17:29:03.313 に答える
1

はい、レコードの追加のランタイム初期化を行うことができます。

var
  A: TProcessOrderRecord;

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

私はあなたがすでに使用しているもののような別の解決策を好むでしょうが。

于 2013-03-06T18:04:12.740 に答える