2013-09-02 4 views
12

for ... in ... doを使用して動的配列を反復処理すると、配列の項目のコピーが作成されますか?例:Delphi動的配列の反復とレコードのコピー

type 
    TSomeRecord =record 
    SomeField1 :string; 
    SomeField2 :string; 
    end; 

var 
    list: array of TSomeRecord; 
    item: TSomeRecord; 

begin 
    // Fill array here 
    for item in list do 
    begin 
     // Is item here a copy of the item in the array or a reference to it? 
    end; 
end; 

ループ内の項目は、配列内の項目またはその参照のコピーですか?

コピーの場合は、コピーを作成せずに配列を反復処理できますか?

おかげで、

AJ

+0

私は、とにかく同様のシナリオでレコードポインタを実装します。レコードを渡して別の場所で内容を変更したいときはいつでも、レコードポインタ 'PSomeRecord'がもっと効率的に機能し、コピーすることを心配する必要はありません。特に内容が多い(メモリが多い)場合。 –

+1

@ジェリーしかし、あなたは明示的に生涯を管理する必要があります –

+0

@David True、それはすべての仕方で大きな変化ですが、私が言ったように、単一のレコードが大量のメモリを消費する場合は、ポインタでそれらを保持することをお勧めします。 –

答えて

11

のループ変数の/ループでループが反復され、その上、容器に保持された値のコピーです。

動的配列の既定の列挙子を置き換えることはできないため、コピーではなく参照を返す列挙子を作成する方法はありません。レコード内で配列を囲む場合は、参照を返すレコードの列挙子を作成することができます。

もちろん、コピーを避けたい場合は、従来のインデックス付きforループを使用できます。コンパイラが参照するのではなくコピーを使用してループに/ために、このような実装することを選択しない理由上記の文のない文書、はないようであるので、


一つは、頼むかもしれません。デザイナーだけが間違いなく答えることができますが、私は正当性を提示することができます。

カスタム列挙子を考えてみましょう。次のようにdocumentation機構を説明:

のためのインクラスまたはインタフェース上のループ構造、 クラスまたはインタフェースが所定の収集パターンを実装する必要がありを使用します。コレクションパターンを実装し 型は、以下の の属性を持っている必要があります

  • クラスまたはインタフェースがGetEnumerator()というパブリックインスタンスメソッドが含まれている必要があります。 GetEnumerator()メソッドは、クラス、 インターフェイス、またはレコードタイプを返す必要があります。
  • GetEnumerator()によって返されるクラス、インターフェイス、またはレコードには、MoveNext()というパブリックインスタンスメソッドが含まれている必要があります。 MoveNext() メソッドは、Booleanを返す必要があります。 for-inループは、最初にこのメソッド を呼び出して、コンテナが空でないことを確認します。
  • GetEnumerator()によって返されるクラス、インターフェイス、またはレコードには、公開インスタンスのCurrentという読み取り専用プロパティが含まれている必要があります。 タイプのCurrentプロパティは、 コレクションに含まれるタイプである必要があります。

カスタム列挙子はCurrentプロパティを使用してコレクションから各値を返します。そしてそれは値がコピーされることを意味します。

したがって、カスタム列挙子は常にコピーを使用します。配列の組み込み列挙子が参照を使用できるかどうかを想像してください。これは、2つのタイプの列挙子の間に意味の相違が生じることになります。設計者が異なるタイプの列挙子のセマンティクス間の一貫性を選択したことは間違いありません。

+0

コピーが作成された場合、そのコピーを変更できないのはなぜですか?たとえば、item.SomeField1:= 'Test'はコンパイルエラーを示します。 –

+1

単純に、言語定義はループ変数を変更できないことを指示します。 –

+1

おそらくこれはコピーであり、コピーへの変更は配列に保存されません。 – VitaliyG

5

@Davidは、列挙子がレコードのコピーであると答えました。

また、レコード内にダイナミックアレイをラップすると、カスタム列挙子が使用できるようになるとも言われました。ここで

がいることをやっての例です:ちょうど完全であることを

program ProjectCustomEnumerator; 

{$APPTYPE CONSOLE} 

type 
    PSomeRecord = ^TSomeRecord; 
    TSomeRecord = record 
    SomeField1 :string; 
    SomeField2 :string; 
    end; 

    TSomeRecordArray = record 
    private type 
    TSomeRecordDynArray = array of TSomeRecord; 
    // For x in .. enumerator 
    TSomeRecordArrayEnumerator = record 
     procedure Create(const AnArray : TSomeRecordDynArray); 
     private 
     FCurrent,FLast : Integer; 
     FArray : TSomeRecordDynArray; 
     function GetCurrent : PSomeRecord; inline; 
     public 
     function MoveNext : Boolean; inline; 
     property Current : PSomeRecord read GetCurrent; 
    end; 
    public 
    List : TSomeRecordDynArray; 
    // Enumerator interface 
    function GetEnumerator : TSomeRecordArrayEnumerator; inline; 
    end; 

procedure TSomeRecordArray.TSomeRecordArrayEnumerator.Create(
    const AnArray: TSomeRecordDynArray); 
begin 
    FCurrent := -1; 
    FLast := Length(AnArray)-1; 
    FArray := AnArray; 
end; 

function TSomeRecordArray.TSomeRecordArrayEnumerator.GetCurrent: PSomeRecord; 
begin 
    Result := @FArray[FCurrent]; 
end; 

function TSomeRecordArray.TSomeRecordArrayEnumerator.MoveNext: Boolean; 
begin 
    Inc(FCurrent); 
    Result := (FCurrent <= FLast); 
end; 

function TSomeRecordArray.GetEnumerator: TSomeRecordArrayEnumerator; 
begin 
    Result.Create(Self.List); 
end; 

var 
    aList : TSomeRecordArray; 
    item : PSomeRecord; 
    i : Integer; 
begin 
    // Fill array here 
    SetLength(aList.List,2); 
    aList.List[0].SomeField1 := 'Ix=0; Field1'; 
    aList.List[0].SomeField2 := 'Ix=0; Field2'; 
    aList.List[1].SomeField1 := 'Ix=1; Field1'; 
    aList.List[1].SomeField2 := 'Ix=1; Field2'; 
    i := -1; 
    for item in aList do 
    begin 
    // Item here a pointer to the item in the array 
    Inc(i); 
    WriteLn('aList index:',i,' Field1:',item^.SomeField1,' Field2:',item^.SomeField2); 
    end; 
    ReadLn; 
end. 

編集

やコメントをフォローアップ、ここで、レコードの任意の動的配列のための一般的なコンテナの例でありますカスタム列挙子。

program ProjectCustomEnumerator; 

{$APPTYPE CONSOLE} 

type 
    PSomeRecord = ^TSomeRecord; 
    TSomeRecord = record 
    SomeField1 :string; 
    SomeField2 :string; 
    end; 

    TRecordArray<T> = record 
    private type 
    TRecordDynArray = array of T; 
    // For x in .. enumerator 
    TRecordArrayEnumerator = record 
     procedure Initialize(const AnArray : TRecordDynArray); 
     private 
     FCurrent,FLast : Integer; 
     FArray : TRecordDynArray; 
     function GetCurrent : Pointer; inline; 
     public 
     function MoveNext : Boolean; inline; 
     property Current : Pointer read GetCurrent; 
    end; 
    public 
    List : TRecordDynArray; 
    // Enumerator interface 
    function GetEnumerator : TRecordArrayEnumerator; inline; 
    end; 

procedure TRecordArray<T>.TRecordArrayEnumerator.Initialize(
    const AnArray: TRecordDynArray); 
begin 
    FCurrent := -1; 
    FLast := Length(AnArray)-1; 
    FArray := AnArray; 
end; 

function TRecordArray<T>.TRecordArrayEnumerator.GetCurrent: Pointer; 
begin 
    Result := @FArray[FCurrent]; 
end; 

function TRecordArray<T>.TRecordArrayEnumerator.MoveNext: Boolean; 
begin 
    Inc(FCurrent); 
    Result := (FCurrent <= FLast); 
end; 

function TRecordArray<T>.GetEnumerator: TRecordArrayEnumerator; 
begin 
    Result.Initialize(Self.List); 
end; 

var 
    aList : TRecordArray<TSomeRecord>; 
    item : PSomeRecord; 
    i : Integer; 
begin 
    // Fill array here 
    SetLength(aList.List,2); 
    aList.List[0].SomeField1 := 'Ix=0; Field1'; 
    aList.List[0].SomeField2 := 'Ix=0; Field2'; 
    aList.List[1].SomeField1 := 'Ix=1; Field1'; 
    aList.List[1].SomeField2 := 'Ix=1; Field2'; 
    i := -1; 
    for item in aList do 
    begin 
    // Item here a pointer to the item in the array 
    Inc(i); 
    WriteLn('aList index:',i,' Field1:',item^.SomeField1,' Field2:',item^.SomeField2); 
    end; 
    ReadLn; 
end. 
+0

+1コメントレコード全体を汎用にすることができ、あらゆるタイプのレコードに使用できます。私はまた、インスタンスメソッドのCreateの使用が混乱していると言います。あまりにもコンストラクタのように見えます。私は、新しい列挙子を返したNewというクラス関数か、Initializeというインスタンスメソッドを持っています。 –

+0

@DavidHeffernan、これを一般的にすると、列挙子は 'PT =^T;'型を返しますが、ループアイテムは 'PSomeRecord'として宣言されます。 '互換性のない型'エラーを避けるには? –

+0

'GetCurrent'の戻り型を' Pointer'として宣言すると、 '互換性のない型'エラーが解決されます。それが最良の解決策であるかどうかは分かりません。 –