Delphiで0ベースの配列を1(任意)ベースの配列に偽装させる

# 0ベースの配列を任意の開始Indexの配列に偽装させるクラス
TCustomBasedArray<T> = class
を
TCustomBasedArray<T> = record
にしてもそのまま動作します。
オブジェクトの開放コードが不要になるのでrecord型が推奨
```pascal: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は不要
```pascal
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;
```
ログの書き込みは、こちらで紹介した関数を使用しています

https://qiita.com/takahasinaoki/items/3e138f70deb6e598eccd