4

FDQuery の結果をレコードの配列に直接コピーする方法はありますか? したがって、これは宣言された型です。

type
  TPerson = record
    id: integer;
    name: string;
    surname: string;
  end;

type
  TPersonList = array of TPerson;

と列を持つSQLite DBがありid, nameますsurname。通常、次のようにその配列に値を追加する必要があります。

var Persons: TPersonList;
begin
Persons[0].id := FDQuery1.FieldByName('id').AsInteger;
....
....
....
end;

しかし、これを行うためのより良い/コンパクトな方法はありますか? 次のようないくつかの機能:

while not FDQuery.eof do begin
    ...
    Persons[i] := FDQuery1[i];
    ...
end;

多分直接またはループで?または、それを行うための関数を作成する必要がありますか? 私には多くの列があり、構造は異なりますが、dbのように正確な構造を持つ多くの異なるレコードタイプがあります。

4

1 に答える 1

7

これを直接行う簡単な方法はありませんが、ソースと実行時のパフォーマンスの両方の効率を向上させるために使用できる手法があります。1 つの方法は、単純なヘルパーを作成して、特定のデータセットから適切な型の新しい値を初期化することです。

これにはレコード メソッドを使用できますが、キャッシュされたフィールド参照の使用が少し洗練されなくなるため、別の専用の初期化クラスを使用することをお勧めします。これは、効率のためにキャッシュされたフィールド参照を使用できます。

type
  TPersonFDInitialiser = class
  private
    fldID: TIntegerField;
    fldName: TStringField;
    fldSurname: TStringField;
    function get_NewValue: TPerson;
  public
    constructor Create(const aSource: TDataset);
    property NewValue: TPerson read get_NewValue;
  end;

コンストラクターでフィールド参照をキャッシュすると、各レコードの値を取得するたびに名前で検索する必要がなくなります。フィールド データ型に適切なクラスを使用すると、変換せずに各フィールド値に直接アクセスできます。

constructor TPersonFDInitialiser.Create(const aSource: TDataset);
begin
  inherited;

  fldID      := aSource.FieldByName('id') as TIntegerField;
  fldName    := aSource.FieldByName('name') as TStringField;
  fldSurname := aSource.FieldByName('surname') as TStringField;
end;


function TPersonFDInitialiser.get_NewValue: TPerson;
begin
  result.ID      := fldID.Value;
  result.Name    := fldName.Value;
  result.Surname := fldSurname.Value;
end;

ご覧のとおり、これは膨大な量の作業ではありません (1 つのレコード値を明示的に初期化するのに必要な作業よりも少し多めです) が、反復的な使用をより洗練されたものにし、次のように書くのがより速くなります:

recno := 0;
init := TPersonFDInitialiser.Create(qryPersons);
try
  while NOT qryPersons.EOF do
  begin
    persons[recno] := init.NewValue;

    qryPersons.Next;
    Inc(recno);
  end;

finally
  init.Free;
end;

個人の行を返し、 TPersonレコードの配列であるTFDQueryはどこqryPersonsにありますか(もちろん、適切にサイズ設定/縮小されています)。persons

TDataset基本クラス ( TFDQueryの最終的な派生) を使用することで、この初期化クラスを、TDatasetの子孫からTPersonを初期化する必要がある任意の場所で使用できますデータセットは書かれているように一貫して名前が付けられていますが、必要に応じて初期化子をこの点でより柔軟にすることができます.これは練習問題として残されています.

もっと遠く行く

必要に応じて、このような初期化クラスのユーティリティを改善するために行うことができる多くの拡張機能があります。たとえば、次のようになります。

// To initialise a one-off, new TPerson value from a data set use a 
//  class function which will internally create an initialiser, obtain 
//   a new TPerson then destroy the initialiser for you:
//
// Note that this will need to be overloaded if it has the same name as
//  the instance method (which must also then be overloaded):

class function TPersonFDInitialiser.NewValue(const aSource: TDataset): TPerson; overload;


// Implement a procedure which will initialise an existing TPerson value
//  (by reference) with values from the current record.
// 
// Again, a class procedure overload could be provided for one-off use
//  taking care of creating and freeing the required initialiser:

class procedure TPersonFDInitialiser.SetPerson(const aSource: TDataset; var aPerson: TPerson); overload;
procedure TPersonFDInitialiser.SetPerson(var aPerson: TPerson); overload;

注: これらのSetPersonメソッドは、get_NewValueが実際にそのメソッドを呼び出すような方法で実装できるため、実装全体で実際に値の設定を行うメソッドは 1 つだけになります。これにより、初期化コードの重複がなくなり、初期化クラスの信頼性と保守性が向上します。

function TPersonFDInitialiser.get_NewValue: TPerson;
begin
  SetPerson(result);
end;


procedure TPersonFDInitialiser.SetPerson(var aPerson: TPerson);
begin
  aPerson.ID      := fldID.Value;
  aPerson.Name    := fldName.Value;
  aPerson.Surname := fldSurname.Value;
end;
于 2014-11-27T03:44:07.260 に答える