for..in

ヘルプには、こんなふうに書かれてますが…

The class must contain a public instance method called GetEnumerator(). The GetEnumerator() method must return a class, interface, or record type.

Win32版のrecordはメソッドを持てないのでCurrentとかMoveNextとか書けません。じゃあ代わりになるものは…と、あれですね。

program D2005Win32ForIn;

{$APPTYPE CONSOLE}
{$WARN SYMBOL_PLATFORM OFF}
{$DEFINE OLD_OBJECT_ENUMERATOR}

type
  TMyRange  = class;

{$IFDEF OLD_OBJECT_ENUMERATOR}
  TMyEnumerator = object
{$ELSE}
  TMyEnumerator = class
{$ENDIF}
  private
    Values: TMyRange;
    FCurrent: Integer;
    function GetCurrent: Integer;
  public
    function MoveNext: Boolean; 
    property Current: Integer read GetCurrent;
  end;

  TMyRange  = class
  private
    FL, FH: Integer;
  public
    constructor Create(L, H: Integer);
    function GetEnumerator: TMyEnumerator;
  end;

{ TMyEnumerator }

function TMyEnumerator.GetCurrent: Integer;
begin
  Result := FCurrent
end;

function TMyEnumerator.MoveNext: Boolean;
begin
  if FCurrent >= Values.FH then
    Result := False
  else
  begin
    Result := True;
    Inc(FCurrent);
  end
end;

{ TMyRange }

constructor TMyRange.Create(L, H: Integer);
begin
  FL := L;
  FH := H;
end;

function TMyRange.GetEnumerator: TMyEnumerator;
begin
{$IFNDEF OLD_OBJECT_ENUMERATOR}
  Result := TMyEnumerator.Create;
{$ENDIF}
  Result.Values := Self;
  Result.FCurrent := FL - 1;
end;

var
  C : TMyRange;
  Item : Integer;
begin
  C := TMyRange.Create(5, 8);
  for Item in C do
    WriteLn(Item);
  C.Free;
  if DebugHook <> 0 then ReadLn;
end.

これが動くんですねー。
しかもclassやinterfaceなら破棄のためにtry..finally...finalize相当の例外処理が挿入されるのに対し、こちらは余計なものが何も挿入されません。特別扱いして実装されてます!?これは確実にこっちのほうがいいですってば。どうせ大半のEnumeratorなんかデストラクタで行わなければならないような処理なんて無いでしょうし。あー、object型のEnumeratorにstringのような後始末の必要な型をフィールドとして持たせるとfor..inで互換性が無いとかなんとか不思議なエラーになりましたねえ。後始末が要るとなったときだけはclass/interfaceにしなければならないようです。
私にはとてもとてもobject型が互換性のためだけに用意されてるとは思えませんねー。
ついでにGetCurrentとかinlineにしようとしたら内部エラー出ましたよははははは。