Tutorial: Implementar IEnumerable(Of T) en Visual Basic
La interfaz IEnumerable<T> se implementa mediante clases que pueden devolver una secuencia de valores, proporcionando los elementos uno a uno. La ventaja de devolver datos proporcionando los elementos uno a uno es que no es necesario cargar el conjunto completo de datos en la memoria para trabajar con él. Solo se usa la memoria suficiente para cargar un único elemento de los datos. Las clases que implementan la interfaz IEnumerable(T)
se pueden usar con bucles For Each
o consultas LINQ.
Pensemos, por ejemplo, en una aplicación que debe leer un archivo de texto grande y devolver cada línea del archivo que coincida con determinados criterios de búsqueda. La aplicación usa una consulta LINQ para devolver líneas del archivo que coincidan con los criterios especificados. Para consultar el contenido del archivo mediante una consulta LINQ, la aplicación podría cargar dicho contenido en una matriz o una colección. Pero cargar todo el archivo en una matriz o una colección consumiría mucho más memoria de la necesaria. En su lugar, la consulta LINQ podría consultar el contenido del archivo mediante una clase enumerable y devolver solo valores que coincidan con los criterios de búsqueda. Las consultas que devuelven solo unos pocos valores coincidentes consumen mucha menos memoria.
Se puede crear una clase que implemente la interfaz IEnumerable<T> para exponer los datos de origen como datos enumerables. La clase que implementa la interfaz IEnumerable(T)
requerirá otra clase que implemente la interfaz IEnumerator<T> para iterar por los datos de origen. Estas dos clases permiten devolver elementos de datos secuencialmente como un tipo específico.
En este tutorial crearemos una clase que implementa la interfaz IEnumerable(Of String)
y una clase que implementa la interfaz IEnumerator(Of String)
para leer línea a línea un archivo de texto.
Nota:
Es posible que tu equipo muestre nombres o ubicaciones diferentes para algunos de los elementos de la interfaz de usuario de Visual Studio en las siguientes instrucciones. La edición de Visual Studio que se tenga y la configuración que se utilice determinan estos elementos. Para obtener más información, vea Personalizar el IDE.
Creación de la clase enumerable
Creación del proyecto de clase enumerable
En el menú Archivo de Visual Basic, elija Nuevo y haga clic en Proyecto.
En el panel Tipos de proyecto del cuadro de diálogo Nuevo proyecto, asegúrese de que esté seleccionado Windows. Seleccione Biblioteca de clases en el panel Plantillas. En el cuadro Nombre, escriba
StreamReaderEnumerable
y haga clic en Aceptar. El nuevo proyecto se abre.En el Explorador de soluciones, haga clic con el botón derecho en el archivo Class1.vb y haga clic en Cambiar nombre. Cambie el nombre del archivo a
StreamReaderEnumerable.vb
y pulse ENTRAR. Al cambiar el nombre del archivo también se cambiará el nombre de la clase aStreamReaderEnumerable
. Esta clase implementará la interfazIEnumerable(Of String)
.En el Explorador de soluciones, haga clic con el botón derecho en el proyecto StreamReaderEnumerable,elija Agregar y, después, haga clic en Nuevo elemento. Seleccione la plantilla Clase. En el cuadro Nombre, escriba
StreamReaderEnumerator.vb
y haga clic en Aceptar.
La primera clase de este proyecto es la clase enumerable e implementará la interfaz IEnumerable(Of String)
. Esta interfaz genérica implementa la interfaz IEnumerable y garantiza que los usuarios de esta clase van a poder acceder a los valores de tipo String
.
Adición del código para implementar IEnumerable
Abra el archivo StreamReaderEnumerable.vb.
En la línea después de
Public Class StreamReaderEnumerable
, escriba lo siguiente y presione ENTRAR.Implements IEnumerable(Of String)
Visual Basic rellena automáticamente la clase con los miembros que la interfaz
IEnumerable(Of String)
necesita.Esta clase enumerable leerá las líneas de un archivo de texto de una en una. Agregue el siguiente código a la clase para exponer un constructor público que toma una ruta de acceso de archivo como parámetro de entrada.
Private _filePath As String Public Sub New(ByVal filePath As String) _filePath = filePath End Sub
La implementación del método GetEnumerator de la interfaz
IEnumerable(Of String)
devolverá una nueva instancia de la claseStreamReaderEnumerator
. La implementación del métodoGetEnumerator
de la interfazIEnumerable
se puede establecer comoPrivate
porque solamente hay que exponer miembros de la interfazIEnumerable(Of String)
. Reemplace el código generado por Visual Basic para los métodosGetEnumerator
por el código siguiente.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
Adición del código para implementar IEnumerator
Abra el archivo StreamReaderEnumerator.vb.
En la línea después de
Public Class StreamReaderEnumerator
, escriba lo siguiente y presione ENTRAR.Implements IEnumerator(Of String)
Visual Basic rellena automáticamente la clase con los miembros que la interfaz
IEnumerator(Of String)
necesita.La clase de enumerador abre el archivo de texto y realiza la E/S de archivo para leer las líneas del archivo. Agregue el siguiente código a la clase para exponer un constructor público que toma una ruta de acceso de archivo como parámetro de entrada y abrir el archivo de texto para leerlo.
Private _sr As IO.StreamReader Public Sub New(ByVal filePath As String) _sr = New IO.StreamReader(filePath) End Sub
Las propiedades
Current
de las interfacesIEnumerator(Of String)
eIEnumerator
devuelven el elemento actual del archivo de texto comoString
. La implementación de la propiedadCurrent
de la interfazIEnumerator
se puede establecer comoPrivate
porque solamente hay que exponer miembros de la interfazIEnumerator(Of String)
. Reemplace el código generado por Visual Basic para las propiedadesCurrent
por el código siguiente.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
El método
MoveNext
de la interfazIEnumerator
se desplaza al siguiente elemento del archivo de texto y actualiza el valor devuelto por la propiedadCurrent
. Si no hay más elementos que leer, el métodoMoveNext
devuelveFalse
; de lo contrario, el métodoMoveNext
devuelveTrue
. Agregue el siguiente código al métodoMoveNext
.Public Function MoveNext() As Boolean _ Implements System.Collections.IEnumerator.MoveNext _current = _sr.ReadLine() If _current Is Nothing Then Return False Return True End Function
El método
Reset
de la interfazIEnumerator
dirige al iterador para que apunte al inicio del archivo de texto y borra el valor del elemento actual. Agregue el siguiente código al métodoReset
.Public Sub Reset() _ Implements System.Collections.IEnumerator.Reset _sr.DiscardBufferedData() _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin) _current = Nothing End Sub
El método
Dispose
de la interfazIEnumerator
garantiza que todos los recursos no administrados se van a liberar antes de que el iterador se destruya. El identificador de archivo que usa el objetoStreamReader
es un recurso no administrado y debe cerrarse antes de que la instancia del iterador se destruya. Reemplace el código generado por Visual Basic para el métodoDispose
por el código siguiente.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
Uso del iterador de ejemplo
Se puede usar una clase enumerable en el código junto con estructuras de control que requieran un objeto que implemente IEnumerable
, como un bucle For Next
o una consulta LINQ. En el siguiente ejemplo se muestra StreamReaderEnumerable
en una consulta LINQ.
Dim adminRequests =
From line In New StreamReaderEnumerable("..\..\log.txt")
Where line.Contains("admin.aspx 401")
Dim results = adminRequests.ToList()