0ベースの配列を任意の開始Indexの配列に偽装させるクラス
TCustomBasedArray = class
を
TCustomBasedArray = record
にしてもそのまま動作します。
オブジェクトの開放コードが不要になるのでrecord型が推奨
CustomBasedArray.pas
unit CustomBasedArray;
interface
uses
System.SysUtils;
type
TEnumerator<T> = class;
TCustomBasedArray<T> = class
private
FArray: TArray<T>;
FStartIndex: Integer;
FSize: Integer;
function GetHigh: Integer;
function GetItem(index: Integer): T;
procedure SetItem(index: Integer; value: T);
public
constructor Create(Size, StartIndex: Integer); overload;
//SourceArrayにTArray<T>型でも互換性あり
constructor Create(const SourceArray: Array of T; StartIndex: Integer); overload;
//for..in ループを使用してイテレーションできるようにする定義
function GetEnumerator: TEnumerator<T>;
property Length: Integer read FSize;
property Low: Integer read FStartIndex;
property High: Integer read GetHigh;
// デフォルトプロパティで配列の要素へのアクセスをサポート
property Items[index: Integer]: T read GetItem write SetItem; default;
end;
//for..in ループを使用してイテレーションできるようにするクラス
TEnumerator<T> = class
private
FData: TCustomBasedArray<T>;
FIndex: Integer;
function GetCurrent: T;
public
constructor Create(const AData: TCustomBasedArray<T>);
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
implementation
{ TCustomBasedArray<T> }
constructor TCustomBasedArray<T>.Create(Size, StartIndex: Integer);
begin
FStartIndex := StartIndex;
FSize := Size;
SetLength(FArray, FSize);
end;
//既存の0ベースの動的配列を開始Indexを指定してコピーする
constructor TCustomBasedArray<T>.Create(const SourceArray: Array of T; StartIndex: Integer);
begin
FStartIndex := StartIndex;
FSize := System.Length(SourceArray);
SetLength(FArray, FSize);
for var i := System.Low(FArray) to System.High(FArray) do
FArray[i] := SourceArray[i];
end;
function TCustomBasedArray<T>.GetHigh: Integer;
begin
Result := FStartIndex + FSize - 1;
end;
function TCustomBasedArray<T>.GetItem(index: Integer): T;
begin
Result := FArray[index - FStartIndex];
end;
procedure TCustomBasedArray<T>.SetItem(index: Integer; value: T);
begin
FArray[index - FStartIndex] := value;
end;
function TCustomBasedArray<T>.GetEnumerator: TEnumerator<T>;
begin
Result := TEnumerator<T>.Create(Self);
end;
{ TEnumerator<T> }
//for..in ループを使用してイテレーションできるようにするクラス
constructor TEnumerator<T>.Create(const AData: TCustomBasedArray<T>);
begin
FData := AData;
FIndex := FData.Low - 1;
end;
function TEnumerator<T>.MoveNext: Boolean;
begin
Inc(FIndex);
Result := FIndex <= FData.High;
end;
function TEnumerator<T>.GetCurrent: T;
begin
Result := FData[FIndex];
end;
end.
使用サンプル
record型にしたらオブジェクトのFreeは不要
procedure TForm1.Button1Click(Sender: TObject);
var
arr: TCustomBasedArray<Integer>;
begin
arr := TCustomBasedArray<Integer>.Create(10, -5);
//要素に値を設定
for var i := arr.Low to arr.High do
arr[i] := i * 10;
for var i := arr.Low to arr.High do
OutputDebugFmt('Index: %d, 値: %d', [i,arr[i]]);
//イテレータのテスト
for var item in arr do
OutputDebugFmt('値: %d', [item]);
arr.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
arr: TCustomBasedArray<string>;
StringArray: TArray<string>;
begin
StringArray := TArray<string>.Create('山田','鈴木', '佐藤');
//偽装クラスに開始Indexを指定してコピー
arr := TCustomBasedArray<string>.Create(StringArray, 10);
for var i := arr.Low to arr.High do
OutputDebugFmt('Index: %d, 値: %s', [i,arr[i]]);
arr.Free;
end;
ログの書き込みは、こちらで紹介した関数を使用しています