.NET - As interface IEnumerable e IEnumerator
A plataforma .NET tem evoluído continuamente fornecendo recursos cada vez mais avançados para o tratamento de coleções. |
Para podermos tratar uma coleção de informações temos que escrever e executar consultas e realizar iterações sobre o resultado.
Quando o assunto é realizar iterações sobre coleções devemos conhecer bem duas interfaces: IEnumerable e IEnumerator.
A interface IEnumerable suporta uma iteração sobre uma coleção não genérica. A interface possui apenas o método GetEnumerator que retorna um enumerador que itera sobre a coleção.
A interface IEnumerator suporta uma iteração simples sobre uma coleção sendo a interface base para todos os enumeradores.
As interfaces IEnumerable e IEnumerator estão no namespace System.Collections.
A hierarquia de interface deste namespace pode ser visto na figura abaixo:
IEnumerable
- É a base direta ou indiretamente para algumas outras Interfaces
e todas as coleções a implementam. Esta interface tem
apenas um método a ser implementado, chamado GetEnumerator,
qual retorna um objeto do tipo da Interface de IEnumerator. IEnumerator - É a base para todos os enumeradores. Composta por uma propriedade Current e dois métodos moveNext e Reset), percorre e lê todos os elementos de uma determinada coleção, permitindo apenas ler os dados, não podendo alterá-los. Vale levar em consideração que enumeradores não podem ser utilizados para a coleção subjacente. ICollection - É a Interface base para todas as coleções que estão contidas dentro do namespace System.Collections. Direta ou indiretamente essas classes a implementa. Ela por sua vez é composta por três propriedades Count, IsSynchronized e SyncRoot e um método CopyTo.
|
A interface IEnumerator possui os seguintes membros:
Abaixo uma figura que mostra como funcionam os membros do IEnumerator:(fonte: http://www.codeproject.com/KB/cs/sssienumerable.aspx)
Vejamos o funcionamentos destes membros:
Um enumerador permanece válido, desde que a coleção permaneça inalterada. Se forem feitas alterações na coleção, como adicionar, modificar ou excluir elementos, o enumerador ficará invalidado e a próxima chamada para MoveNext ou Reset gera uma exceção InvalidOperationException. Se a coleção é modificado entre MoveNext e Current, Current retornará o elemento que é definido, mesmo se o enumerador está já invalidado.
A instrução foreach (linguagem C#)/for each (Visual Basic) oculta a complexidade dos enumeradores. Portanto é recomendável usar foreach/for each ao invés de tentar manipular diretamente o enumerador. (Os enumeradores podem ser usados para ler dados em um coleção mas não podem modificar estes dados.)
Implementando as interface IEnumerable e IEnumerator
Crie um novo projeto usando o Visual Basic 2010 Express Edition do tipo Console Application com o nome IEnumerable_Exemplo1:
Public Class TimesEnumerator Implements IEnumerator Private times() As String = {"Santos", "São Paulo", "Palmeiras", "Corintians", "Flamengo", "Vasco"} Private posicao As Integer = -1 Public ReadOnly Property Current() As Object Implements IEnumerator.Current Get If Me.posicao < 0 OrElse Me.posicao > 4 Then Throw New InvalidOperationException Else Return Me.times(Me.posicao) End If End Get End Property Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext Me.posicao += 1 Return Me.posicao <= 4 End Function Public Sub Reset() Implements IEnumerator.Reset Me.posicao = -1 End Sub End Class |
No código acima criamos a classe TimesEnumerator e implementamos a interface IEnumerator; dessa forma tivemos que definir os métodos: MoveNext() e Reset() e a propriedade Current.
Se você quiser exibir os valores do array de strings times() terá que usar um laço for/next e verificar se o método retorna True ou False para em seguida usar a propriedade Current para recuperar o item da coleção.
Uma maneira mais simples seria usar um laço For/Each mas para isso teremos que criar uma classe auxiliar implementando a interface IEnumerable que possui o método GetEnumerator que retornará uma instância da classe TimesEnumerator.
Public Class Times Implements IEnumerable Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return New TimesEnumerator() End Function End Class |
Agora já podemos usar o laço For/Each para iterar sobre a coleção:
For Each
time As String In New Times() Console.WriteLine(time) Next |
Executando o projeto teremos o seguinte resultado:
Vamos então criar outro exemplo mais completo usando ambas a interfaces e o laço For/Each.
Vejamos um exemplo simples de implementação das interfaces IEnumerable e IEnumerator para uma coleção customizada. No exemplo os membros destas interfaces foram implementados para realizar a iteração sobre uma coleção usando for each.
Crie um novo projeto usando o Visual Basic 2010 Express Edition do tipo Console Application com o nome IEnumerable_Exemplo2:
Imports System.Collections Module Module1 Sub Main() Dim pessoasArray() As Pessoa = { _ New Pessoa("Macoratti", "macoratti@yahoo.com"), _ New Pessoa("Miriam", "miriam@hotmail.com"), _ New Pessoa("Jefferson", "jeff@bol.com.br")} Dim pessoasNaLista As New Pessoas(pessoasArray) Dim p As Pessoa For Each p In pessoasNaLista Console.WriteLine(p.Nome + " " + p.Email) Next Console.ReadKey() End Sub Public Class Pessoa Public Sub New(ByVal _nome As String, ByVal _email As String) Me.Nome = _nome Me.Email = _email End Sub Public Nome As String Public Email As String End Class Public Class Pessoas Implements IEnumerable Private _pessoas() As Pessoa Public Sub New(ByVal pArray() As Pessoa) _pessoas = New Pessoa(pArray.Length - 1) {} Dim i As Integer For i = 0 To pArray.Length - 1 _pessoas(i) = pArray(i) Next i End Sub Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return New PessoasEnum(_pessoas) End Function End Class Public Class PessoasEnum Implements IEnumerator Public _pessoas() As Pessoa ' Enumeradores são posicionados antes do primeiro elemento ' ate que o m´metodo MoveNext() seja chamado Dim posicao As Integer = -1 Public Sub New(ByVal lista() As Pessoa) _pessoas = lista End Sub Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext posicao = posicao + 1 Return (posicao < _pessoas.Length) End Function Public Sub Reset() Implements IEnumerator.Reset posicao = -1 End Sub Public ReadOnly Property Current() As Object Implements IEnumerator.Current Get Try Return _pessoas(posicao) Catch ex As IndexOutOfRangeException Throw New InvalidOperationException() End Try End Get End Property End Class End Module |
||
Criamos a classe Pessoa
contendo as propriedades :
Criamos a classe Pessoas que implementa a interface IEnumerable. Nesta classe definimos o método GetEnumerator() Definimos a classe PessoaEnum que implementa a interface IEnumerator Nesta classe definimos os métodos:
e a propriedade
No método Main()
instanciamos a classe Pessoa e criamos
alguns objetos do Criamos uma instância da classe Pessoas passando o array de pessoas. A seguir percorremos a coleção usando um For Each. Executando o projeto teremos o seguinte resultado: |
Se você conhece a sintaxe da linguagem Visual Basic deve lembrar que para percorrer uma coleção fazíamos assim:
For i = 1 to
fimdaColeção
'
Fazer alguma coisa com a Coleção(i)
Next i
No Visual Basic .NET temos a sintaxe:
For Each
elemento in Coleção
' Fazer alguma
coisa com o elemento
Next element
Uma das razões pela qual a sintaxe VB .NET - For Each - é melhor é que você não tem que saber onde a coleção começa e termina. O VB .NET faz isso para você.
Pegue o projeto completo aqui: IEnumerable_Exemplo.zip
"Eu sou a luz que vim ao mundo, para que todo aquele que crê em mim não permaneça nas trevas." João 12:46
Referências: