3

プライベート静的配列の TEnumerator でビルドを公開しようとしています。

Delphi自体は静的配列を直接列挙できるため(以下を参照)、Delphiが静的配列のバックグラウンドで列挙子を作成すると思われ、GetEnumeratorメソッドで同じ列挙子を作成して公開できることを望んでいます。

(私はDelphi XE2を使用しています)。

program Project6;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Generics.Collections;

type
  TMyEnum = (meA, meB);

  TMyClass = class
  private
    FItems: array[TMyEnum] of Integer;
  protected
  public
    function GetEnumerator: TEnumerator<Integer>;
  end;

{ TMyClass }

function TMyClass.GetEnumerator: TEnumerator<Integer>;
begin
  // What is the simplies way of creating this enumerator?
end;

var
  myObj: TMyClass;
  i: Integer;

begin
  myObj := TMyClass.Create;
  try
    // This works but only in the same unit
    for i in myObj.FItems do
      WriteLn(i);

    for i in myObj do
      WriteLn(i);

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  ReadLn;
end.

以下のようなカスタムエミュレーターを作成できることに注意してください。しかし、私はこれを避けて、組み込みのものを公開しようとしています。

TStaticArrayEnumerator<T> = class(TEnumerator<T>)
  private
    FCurrent: Pointer;
    FElementAfterLast: Pointer;
  protected
    function DoGetCurrent: T; override;
    function DoMoveNext: Boolean; override;
  public
    constructor Create(aArray: Pointer; aCount: Integer);
  end;

{ TStaticArrayEnumerator<T> }

constructor TStaticArrayEnumerator<T>.Create(aArray: Pointer; aCount: Integer);
begin
  // need to point Current before the first element (see comment in DoMoveNext)
  FCurrent := Pointer(NativeInt(aArray) - SizeOf(T));
  FElementSize := aElementSize;
  FElementAfterLast := Pointer(NativeInt(aArray) + aCount * SizeOf(T))
end;

function TStaticArrayEnumerator<T>.DoGetCurrent: T;
begin
  Result := T(FCurrent^);
end;

function TStaticArrayEnumerator<T>.DoMoveNext: Boolean;
begin
  // This method gets called before DoGetCurrent gets called the first time
  FCurrent := Pointer(NativeInt(FCurrent) + SizeOf(T));

  Result := not (FCurrent = FElementAfterLast);
end;
4

2 に答える 2

2

David が指摘したように、配列には組み込みの列挙子型はなく、実装は基本的に単純なループを偽装する構文糖衣です。

あなたのクラスを列挙可能にする別の方法を提案するには、あなたTMyEnumが連続している場合(そうであるように思われます)、および必ずしも一般的な実装を探しているわけではない場合(明確ではありません):

type
  TMyEnum = (meA, meB);
  TMyItems = array[TMyEnum] of Integer;

  TMyItemsEnum = class
  private
    FGotFirst : boolean;
    FOwner: TMyItems;
    FCurrent : TMyEnum;
  public
    constructor Create(owner: TMyItems);
    function GetCurrent: Integer;
    function MoveNext: boolean;
    property Current: Integer read GetCurrent;
  end;

  TMyClass = class(TObject)
  private
    FItems: TMyItems;
  public
    function GetEnumerator : TMyItemsEnum;
  end;

次のように実装します。

constructor TMyItemsEnum.Create(owner: TMyItems);
begin
  FOwner := owner;
  FGotFirst := false;
  FCurrent := TMyEnum(Low(TMyEnum));
end;

function TMyItemsEnum.GetCurrent: Integer;
begin
  Result := FOwner[FCurrent];
end;

function TMyItemsEnum.MoveNext: boolean;
begin
  Result := false;
  if not FGotFirst then begin
    FGotFirst := true;
    Result := true;
  end else begin
    if Ord(FCurrent) < Ord(High(TMyEnum)) then begin
      FCurrent := TMyEnum(Succ(FCurrent));
      Result := true;
    end;
  end;
end;

function TMyClass.GetEnumerator : TMyItemsEnum;
begin
  result := TMyItemsEnum.Create(FItems);
end;

例 :

var
  myObj: TMyClass;
  i: Integer;

begin
  myObj := TMyClass.Create;
  myObj.FItems[meA] := 123;
  myObj.FItems[meB] := 456;

  for i in myObj do WriteLn(i);
end.

列挙型が連続していない場合、つまり:

 TMyEnum = (meA = 3, meB = 17);

明らかに、実装は機能しません。またTMyItems、列挙値の間に(インデックス可能な)空のスペースを含む静的配列を作成するため、これが最終的に有用になる可能性はかなり低いようです。いずれにせよ、これはあなたの質問の一部ではないので、上記で十分です。

于 2013-12-30T21:08:47.330 に答える