質問の背景:
Visual BasicのFor Each...Next文は、コレクションや配列の要素を反復処理することを可能にします。VB6からは標準コレクションの反復処理がサポートされ、VB.NETでは特定のインターフェースを実装するすべての型に対応しています。しかし、独自のコレクションを作成して独自の反復処理を行う必要がある場合、必要なインフラストラクチャを正しく実装することが重要です。
問題
要素の保存だけを実装するだけでは不十分です。For Each文は、IEnumerableインターフェースのサポートを必要とします。しばしば、カスタムコレクションの実装はAdd/Getメソッドに制限され、オブジェクトは反復処理のための「ブラックボックス」と化のります。これは、反復処理の言語構造との統合を妨げ、エラーを引き起こします。
解決策
For Eachがあなたのコレクションで正しく動作するためには、IEnumerableインターフェースを実装し、型指定要素との連携をサポートするためにIEnumerable(Of T)を実装する必要があります。また、IEnumeratorインターフェースを実装する独自の列挙子(Enumerator)を作成する必要があります(またはジェネリックバージョンの場合はIEnumerator(Of T)を)。
例:
Public Class IntCollection Implements IEnumerable(Of Integer) Private ReadOnly items As New List(Of Integer)() Public Sub Add(value As Integer) items.Add(value) End Sub Public Function GetEnumerator() As IEnumerator(Of Integer) _ Implements IEnumerable(Of Integer).GetEnumerator Return items.GetEnumerator() End Function Private Function IEnumerable_GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return GetEnumerator() End Function End Class ' 使用方法: Dim col As New IntCollection() col.Add(10) col.Add(20) col.Add(30) For Each n As Integer In col Console.WriteLine(n) Next
主な特徴:
インターフェースIEnumerableなしで、単にGetEnumeratorメソッドを書くだけでFor Eachを実装できますか?
いいえ、Visual BasicはCollectionsがFor Eachと互換性を持つとコンパイラが認識するために、IEnumerable/IEnumerable(Of T)インターフェースの公式サポートを必要とします。
列挙子は別のクラスである必要がありますか?
いいえ、要素データベースが標準の列挙子をサポートしている場合(例えば、List(Of T)を集約している場合)、そのGetEnumeratorを返すことができます。複雑なシナリオの場合は、独自のIEnumerator実装クラスが必要な場合があります。
For Eachループ内でコレクションを変更することは可能ですか?
いいえ、反復中にコレクションを変更するとInvalidOperationExceptionが発生します。適切な処理には特別な戦略(例:反復処理前にリストをコピーする、またはインデクシングを使用する)を必要とします。
会社では、IEnumerableインターフェースを実装せずに、配列を介して追加および削除するMyCollectionクラスを独自に開発しました。しかし、For Eachは機能せず、通常のループと要素を含む公開フィールドを使用する必要がありました。
利点:
欠点:
クラスを改造し、IEnumerable(Of T)を実装しました。その後、コレクションはFor Eachに標準的に簡単に使用でき、LINQとの互換性も確保されました。
利点:
欠点: