次の方法で共有


チュートリアル: Visual Basic での IEnumerable(Of T) の実装

IEnumerable<T> インターフェイスは、一度に 1 つの項目の値のシーケンスを返すことができるクラスによって実装されます。 一度に 1 つの項目のデータを返す利点は、処理するためにメモリにデータの完全なセットを読み込む必要がないということです。 データから 1 つの項目を読み込むのに十分なメモリを使用するだけで済みます。 IEnumerable(T) インターフェイスを実装するクラスは、For Each ループまたは LINQ クエリで使用できます。

たとえば、大きなテキスト ファイルを読み取り、特定の検索条件に一致するファイルから各行を返す必要があるアプリケーションがあるとします。 アプリケーションは LINQ クエリを使用して、指定された条件に一致する行をファイルから返します。 LINQ クエリを使用してファイルの内容に対してクエリを実行するために、アプリケーションはファイルの内容を配列またはコレクションに読み込むことができます。 ただし、配列またはコレクションにファイル全体を読み込むと、必要以上に多くのメモリが消費されます。 LINQ クエリでは、代わりに列挙可能なクラスを使用してファイルの内容を照会し、検索条件に一致する値のみを返すことができます。 一致する値が少数しか返されていないクエリでは、消費されるメモリがはるかに少なくなります。

IEnumerable<T> インターフェイスを実装するクラスを作成して、ソース データを列挙可能なデータとして公開できます。 IEnumerable(T) インターフェイスを実装するクラスには、ソース データを反復処理するためにIEnumerator<T> インターフェイスを実装する別のクラスが必要です。 これら 2 つのクラスを使用すると、データの項目を特定の型として順番に返します。

このチュートリアルでは、 IEnumerable(Of String) インターフェイスを実装するクラスと、テキスト ファイルを一度に 1 行ずつ読み取る IEnumerator(Of String) インターフェイスを実装するクラスを作成します。

次の手順では、一部の Visual Studio ユーザー インターフェイス要素の名前や場所がコンピューターに異なる場合があります。 これらの要素は、使用している Visual Studio エディションと使用する設定によって決まります。 詳細については、「IDEのカスタマイズ」を参照してください。

列挙可能なクラスの作成

列挙可能なクラス プロジェクトを作成する

  1. Visual Basic の [ ファイル ] メニューの [ 新規作成 ] をポイントし、[ プロジェクト] をクリックします。

  2. [ 新しいプロジェクト ] ダイアログ ボックスの [ プロジェクトの種類 ] ウィンドウで、 Windows が選択されていることを確認します。 [テンプレート] ウィンドウで [クラス ライブラリ] を選択します。 [ 名前 ] ボックスに「 StreamReaderEnumerable」と入力し、[OK] をクリック します。 新しいプロジェクトが表示されます。

  3. ソリューション エクスプローラーで、Class1.vb ファイルを右クリックし、[名前の変更] をクリックします。 ファイルの名前を StreamReaderEnumerable.vb に変更し、Enter キーを押します。 ファイルの名前を変更すると、クラスの名前も StreamReaderEnumerable に変更されます。 このクラスは、 IEnumerable(Of String) インターフェイスを実装します。

  4. StreamReaderEnumerable プロジェクトを右クリックし、[ 追加] をポイントして、[ 新しい項目] をクリックします。 クラス テンプレートを選択します。 [ 名前 ] ボックスに「 StreamReaderEnumerator.vb 」と入力し、[OK] をクリック します

このプロジェクトの最初のクラスは列挙可能なクラスであり、 IEnumerable(Of String) インターフェイスを実装します。 このジェネリック インターフェイスは、 IEnumerable インターフェイスを実装し、このクラスのコンシューマーが Stringとして型指定された値にアクセスできることを保証します。

IEnumerable を実装するコードを追加する

  1. StreamReaderEnumerable.vb ファイルを開きます。

  2. Public Class StreamReaderEnumerable後の行で、次のように入力し、Enter キーを押します。

    Implements IEnumerable(Of String)
    

    Visual Basic では、 IEnumerable(Of String) インターフェイスに必要なメンバーがクラスに自動的に設定されます。

  3. この列挙可能なクラスは、テキスト ファイルから一度に 1 行ずつ行を読み取ります。 次のコードをクラスに追加して、ファイル パスを入力パラメーターとして受け取るパブリック コンストラクターを公開します。

    Private _filePath As String
    
    Public Sub New(ByVal filePath As String)
        _filePath = filePath
    End Sub
    
  4. IEnumerable(Of String) インターフェイスのGetEnumerator メソッドを実装すると、StreamReaderEnumerator クラスの新しいインスタンスが返されます。 IEnumerable インターフェイスの GetEnumerator メソッドの実装は、IEnumerable(Of String) インターフェイスのメンバーのみを公開する必要があるため、Privateできます。 GetEnumerator メソッドに対して Visual Basic によって生成されたコードを次のコードに置き換えます。

    Public Function GetEnumerator() As IEnumerator(Of String) _
        Implements IEnumerable(Of String).GetEnumerator
    
        Return New StreamReaderEnumerator(_filePath)
    End Function
    
    Private Function GetEnumerator1() As IEnumerator _
        Implements IEnumerable.GetEnumerator
    
        Return Me.GetEnumerator()
    End Function
    

IEnumerator を実装するコードを追加する

  1. StreamReaderEnumerator.vb ファイルを開きます。

  2. Public Class StreamReaderEnumerator後の行で、次のように入力し、Enter キーを押します。

    Implements IEnumerator(Of String)
    

    Visual Basic では、 IEnumerator(Of String) インターフェイスに必要なメンバーがクラスに自動的に設定されます。

  3. 列挙子クラスは、テキスト ファイルを開き、ファイル I/O を実行してファイルから行を読み取ります。 次のコードをクラスに追加して、ファイル パスを入力パラメーターとして受け取り、読み取り用にテキスト ファイルを開くパブリック コンストラクターを公開します。

    Private _sr As IO.StreamReader
    
    Public Sub New(ByVal filePath As String)
        _sr = New IO.StreamReader(filePath)
    End Sub
    
  4. IEnumerator(Of String)インターフェイスとIEnumerator インターフェイスの両方のCurrentプロパティは、テキスト ファイルから現在の項目をStringとして返します。 IEnumerator(Of String) インターフェイスのメンバーのみを公開する必要があるため、IEnumerator インターフェイスの Current プロパティの実装をPrivateできます。 Current プロパティに対して Visual Basic によって生成されたコードを次のコードに置き換えます。

    Private _current As String
    
    Public ReadOnly Property Current() As String _
        Implements IEnumerator(Of String).Current
    
        Get
            If _sr Is Nothing OrElse _current Is Nothing Then
                Throw New InvalidOperationException()
            End If
    
            Return _current
        End Get
    End Property
    
    Private ReadOnly Property Current1() As Object _
        Implements IEnumerator.Current
    
        Get
            Return Me.Current
        End Get
    End Property
    
  5. IEnumerator インターフェイスの MoveNext メソッドは、テキスト ファイル内の次の項目に移動し、Current プロパティによって返される値を更新します。 読み取る項目がそれ以上ない場合、 MoveNext メソッドは Falseを返します。それ以外の場合、 MoveNext メソッドは Trueを返します。 MoveNext メソッドに次のコードを追加します。

    Public Function MoveNext() As Boolean _
        Implements System.Collections.IEnumerator.MoveNext
    
        _current = _sr.ReadLine()
        If _current Is Nothing Then Return False
        Return True
    End Function
    
  6. IEnumerator インターフェイスの Reset メソッドは、テキスト ファイルの先頭を指す反復子を指示し、現在の項目の値をクリアします。 Reset メソッドに次のコードを追加します。

    Public Sub Reset() _
        Implements System.Collections.IEnumerator.Reset
    
        _sr.DiscardBufferedData()
        _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
        _current = Nothing
    End Sub
    
  7. IEnumerator インターフェイスのDispose メソッドは、反復子が破棄される前にすべてのアンマネージ リソースが解放されることを保証します。 StreamReader オブジェクトによって使用されるファイル ハンドルはアンマネージ リソースであり、反復子インスタンスが破棄される前に閉じる必要があります。 Dispose メソッドに対して Visual Basic によって生成されたコードを次のコードに置き換えます。

    Private disposedValue As Boolean = False
    
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' Dispose of managed resources.
            End If
            _current = Nothing
            _sr.Close()
            _sr.Dispose()
        End If
    
        Me.disposedValue = True
    End Sub
    
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
    

サンプル反復子の使用

コード内で列挙可能なクラスを、For Next ループや LINQ クエリなど、IEnumerableを実装するオブジェクトを必要とするコントロール構造体と共に使用できます。 次の例は、LINQ クエリの StreamReaderEnumerable を示しています。

Dim adminRequests =
    From line In New StreamReaderEnumerable("..\..\log.txt")
    Where line.Contains("admin.aspx 401")

Dim results = adminRequests.ToList()

こちらも参照ください