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にしようとしたら内部エラー出ましたよははははは。