« ■Webセミナーに関するアンケート | トップページ | ☆TListを使ったリスト No2 »

☆TListを使ったリスト No1

その昔、ポインタを繋いだり、繋ぎ変えたり、ガベージコレクションさせたりと誰もが自作リストでクラス(当時はレコード型だったかな)を管理していました。しかしDelphiには、TListという汎用性のあるリストが用意されていますので、わざわざ可読性の悪いリストを自作する必要はないです。

さて、そのTListですがポインタを扱うので、次のように型キャストが必要になります。
procedure TForm1.Button1Click(Sender: TObject);
var
  List: TList;
  I: Integer;
begin
  List := TList.Create;
  try
    List.Add(TStringList.Create);
    TStringList(List[0]).Text := 'delphi-fan';
    Memo1.Lines.Assign(TStringList(List[0]));
  finally
    for I := 0 to List.Count - 1 do
      TStringList(List[I]).Free;
    List.Free;
  end;
end;

これだとやはり可読性がよくない!ということで、特定のクラスを管理するリストを作ってみたいと思います。 人によっては、MyList = class(TList)という形で作られておられますが、 この場合、TListのメンバーに直接アクセスできる、すなわちポインタとしても扱えるためどんな型も 管理することができてしまいます。となるとバグが潜む可能性が出てきますよね。
(この方法を否定しているわけではないです)
ということで、私はTListをprivateなフィールドにしたクラスが好きです(笑)

せっかくなので拡張性を持たせたサンプルを作っていきたいと思います。
リストの読み書きを汎用ポインタではなく、THRBaseクラスで行えるようにしています。
unit HRBaseClasses;

interface

uses
  SysUtils, Classes;

type

  THRBase = class(TPersistent);

  THRBaseList = class(TPersistent)
  private
    FList: TList;                                       { リスト }
    function GetData(Index: Integer): THRBase;          { 読み込み }
    procedure SetData(Index: Integer; HRBase: THRBase); { 書き込み }
    function GetCount: Integer;                         { リストのカウント数 }
  protected
    procedure Error;                                    { エラーの表示 }
    property List[Index: Integer]: THRBase              { リストへのアクセス }
      read GetData write SetData; default;
  public
    constructor Create;                                 { 生成 }
    destructor Destroy; override;                       { 破棄 }
    procedure Clear;                                    { 消去 }
    function Add(HRBase: THRBase): Integer;             { 追加 }
    procedure Insert(Index: Integer; HRBase: THRBase);  { 挿入 }
    procedure Delete(Index: Integer);                   { 削除 }
    procedure Assign(Source: TPersistent); override;    { Assign }
  published
    property Count: Integer read GetCount;              { リストのカウント数 }
  end;

implementation

{ THRBaseList }

// 生成
constructor THRBaseList.Create;
begin
  FList := TList.Create;
end;

// 破棄
destructor THRBaseList.Destroy;
begin
  Clear;
  FList.Free;
  inherited Destroy;
end;

// 消去
procedure THRBaseList.Clear;
var
  I: Integer;
begin
  for I := 0 to FList.Count -1 do
    THRBase(FList[I]).Free;
  FList.Clear;
end;

// 追加
function THRBaseList.Add(HRBase: THRBase): Integer;
begin
  Result := FList.Add(HRBase);
end;

// 挿入
procedure THRBaseList.Insert(Index: Integer; HRBase: THRBase);
begin
  FList.Insert(Index, HRBase);
end;

// 削除
procedure THRBaseList.Delete(Index: Integer);
begin
  THRBase(FList[Index]).Free;
  FList.Delete(Index);
end;

// エラー処理
procedure THRBaseList.Error;
begin
  raise Exception.Create('インデックスがリストの範囲を超えています');
end;

// リストからの取得
function THRBaseList.GetData(Index: Integer): THRBase;
begin
  if (Index < 0) or (Index >= FList.Count) then Error;
  Result := THRBase(FList[Index]);
end;

// リストへの設定
procedure THRBaseList.SetData(Index: Integer; HRBase: THRBase);
begin
  if (Index < 0) or (Index >= FList.Count) then Error;
  FList[Index] := HRBase;
end;

// リストのカウント数
function THRBaseList.GetCount: Integer;
begin
  Result := FList.Count;
end;

// Assign
procedure THRBaseList.Assign(Source: TPersistent);
var
  I: Integer;
  HRBase: THRBase;
begin
  if (Source is THRBaseList) then
  begin
    Clear;
    for I := 0 to (Source as THRBaseList).Count -1 do
    begin
      HRBase := THRBase.Create;
      HRBase.Assign((Source as THRBaseList)[I]);
      Add(HRBase);
    end;
    Exit;
  end;
  inherited Assign(Source);
end;

end.

なぜTObjectではなくて、TPersistentから継承しているのか、疑問に思われる方も多いかも知れませんね。 その理由は、私が作るリストの中では、TPersistentのAssignという仮想メソッドを再定義することが多いからです(笑)
※procedure Assign(Source: TPersistent); override; という部分ですね。

今回は、単純なクラスを拡張していくという目的で作っているので、少し回りくどい処理になっているかも知れません。 HRBaseClassesを取り込んだ形でいきなり「TListを使ったリスト No2」のMySampleClassesにあるクラスを作ることも多いです。

TListを使ったリスト No1
TListを使ったリスト No2
TListを使ったリスト No3

|

« ■Webセミナーに関するアンケート | トップページ | ☆TListを使ったリスト No2 »