Instruções passo a passo: implementando IEnumerable(Of T) no Visual Basic

A interface IEnumerable<T> é implementada por classes que podem retornar uma sequência de valores de um item por vez. A vantagem de retornar dados um item de cada vez é que você não precisa carregar o conjunto completo de dados na memória para trabalhar com ele. Você só precisa usar memória suficiente para carregar um único item dos dados. Classes que implementam a interface IEnumerable(T) podem ser usadas com loops For Each ou consultas LINQ.

Por exemplo, considere um aplicativo que precisa ler um arquivo de texto grande e retornar cada linha do arquivo que corresponda a critérios de pesquisa específicos. O aplicativo usa uma consulta LINQ para retornar linhas do arquivo que correspondem aos critérios especificados. Para consultar o conteúdo do arquivo usando uma consulta LINQ, o aplicativo pode carregar o conteúdo do arquivo em uma matriz ou coleção. No entanto, carregar o arquivo inteiro em uma matriz ou coleção consumiria muito mais memória do que o necessário. Em vez disso, a consulta LINQ poderia consultar o conteúdo do arquivo usando uma classe enumerável, retornando apenas valores que correspondem aos critérios de pesquisa. Consultas que retornam apenas alguns valores correspondentes consumiriam muito menos memória.

Você pode criar uma classe que implementa a interface IEnumerable<T> para expor dados de origem como dados enumeráveis. Sua classe que implementa a interface IEnumerable(T) exigirá outra classe que implemente a interface IEnumerator<T> para iterar por meio dos dados de origem. Essas duas classes permitem que você retorne itens de dados sequencialmente como um tipo específico.

Neste passo a passo, você criará uma classe que implementa a interface IEnumerable(Of String) e uma classe que implementa a interface IEnumerator(Of String) para ler um arquivo de texto uma linha de cada vez.

Observação

Seu computador pode mostrar diferentes nomes ou locais para alguns dos elementos de interface do usuário do Visual Studio nas instruções a seguir. A edição do Visual Studio que você possui e as configurações que você usa determinam esses elementos. Para obter mais informações, consulte Personalizando o IDE.

Criando a Classe Enumerável

Criar o projeto de classe enumerável

  1. No Visual Basic, no menu Arquivo, aponte para Novo e clique em Projeto.

  2. Na caixa de diálogo Novo Projeto, no painel Tipos de Projetos, certifique-se de que Windows esteja selecionado. Selecione Biblioteca de Classes no painel Modelos. Na caixa Nome, digite StreamReaderEnumerable e clique em OK. O novo projeto é exibido.

  3. Em Gerenciador de Soluções, clique com o botão direito do mouse no arquivo Class1.vb e clique em Renomear. Renomeie o arquivo como StreamReaderEnumerable.vb e pressione ENTER. Renomear o arquivo também renomeará a classe para StreamReaderEnumerable. Essa classe implementará a interface IEnumerable(Of String).

  4. Clique com o botão direito do mouse no projeto StreamReaderEnumerable, aponte para Adicionar e clique em Novo Item. Selecione o modelo de Classe. Na caixa Nome, digite StreamReaderEnumerator.vb e clique em OK.

A primeira classe neste projeto é a classe enumerável e implementará a interface IEnumerable(Of String). Essa interface genérica implementa a interface IEnumerable e garante que os consumidores dessa classe possam acessar valores digitados como String.

Adicionar o código para implementar o IEnumerable

  1. Abra o arquivo StreamReaderEnumerable.vb.

  2. Na linha após Public Class StreamReaderEnumerable, digite o que vem a seguir e pressione ENTER.

    Implements IEnumerable(Of String)
    

    O Visual Basic preenche automaticamente a classe com os membros que são exigidos pela interface IEnumerable(Of String).

  3. Essa classe enumerável lerá linhas de um arquivo de texto uma linha de cada vez. Adicione o código a seguir à classe para expor um construtor público que usa um caminho de arquivo como um parâmetro de entrada.

    Private _filePath As String
    
    Public Sub New(ByVal filePath As String)
        _filePath = filePath
    End Sub
    
  4. Sua implementação do método GetEnumerator da interface IEnumerable(Of String) retornará uma nova instância da classe StreamReaderEnumerator. A implementação do método GetEnumerator da interface IEnumerable pode ser feita Private, porque você precisa expor apenas membros da interface IEnumerable(Of String). Substitua o código que o Visual Basic gerou para os métodos GetEnumerator pelo código a seguir.

    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
    

Adicionar o código para implementar o IEnumerator

  1. Abra o arquivo StreamReaderEnumerator.vb.

  2. Na linha após Public Class StreamReaderEnumerator, digite o que vem a seguir e pressione ENTER.

    Implements IEnumerator(Of String)
    

    O Visual Basic preenche automaticamente a classe com os membros que são exigidos pela interface IEnumerator(Of String).

  3. A classe enumerador abre o arquivo de texto e executa a E/S do arquivo para ler as linhas do arquivo. Adicione o código a seguir à classe para expor um construtor público que usa um caminho de arquivo como um parâmetro de entrada e abra o arquivo de texto para leitura.

    Private _sr As IO.StreamReader
    
    Public Sub New(ByVal filePath As String)
        _sr = New IO.StreamReader(filePath)
    End Sub
    
  4. As Current propriedades para as interfaces IEnumerator(Of String) e IEnumerator retornam o item atual do arquivo de texto como um String. A implementação da propriedade Current da interface IEnumerator pode ser feita Private, porque você precisa expor apenas membros da interface IEnumerator(Of String). Substitua o código que o Visual Basic gerou para as propriedades Current pelo código a seguir.

    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. O método MoveNext da interface IEnumerator navega até o próximo item no arquivo de texto e atualiza o valor retornado pela propriedade Current. Se não houver mais itens a serem lidos, o método MoveNext retornará False; caso contrário, o método MoveNext retornará True. Adicione o seguinte código ao MoveNext método.

    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. O método Reset da interface IEnumerator orienta o iterador a apontar para o início do arquivo de texto e limpa o valor do item atual. Adicione o seguinte código ao Reset método.

    Public Sub Reset() _
        Implements System.Collections.IEnumerator.Reset
    
        _sr.DiscardBufferedData()
        _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
        _current = Nothing
    End Sub
    
  7. O método Dispose da interface IEnumerator garante que todos os recursos não gerenciados sejam liberados antes que o iterador seja destruído. O identificador de arquivo usado pelo objeto StreamReader é um recurso não gerenciado e precisa ser fechado antes que a instância do iterador seja destruída. Substitua o código que o Visual Basic gerou para o método Dispose pelo código a seguir.

    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
    

Usando o Iterador de Amostra

Você pode usar uma classe enumerável em seu código junto com estruturas de controle que exigem um objeto que implementa IEnumerable, como um loop For Next ou uma consulta LINQ. O exemplo a seguir mostra o StreamReaderEnumerable em uma consulta LINQ.

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

Dim results = adminRequests.ToList()

Confira também