Historia de la pregunta:
El operador For Each...Next en Visual Basic permite iterar sobre los elementos de colecciones o matrices. Desde VB6 se soportó la iteración sobre colecciones estándar, y en VB.NET — sobre todos los tipos que implementan una interfaz específica. Sin embargo, cuando se requieren crear colecciones personalizadas para la iteración, es importante implementar correctamente la infraestructura necesaria.
Problema
No es suficiente con implementar el almacenamiento de elementos: los operadores For Each requieren el soporte de la interfaz especializada IEnumerable. A menudo, las colecciones personalizadas se limitaban a los métodos Add/Get, transformando el objeto en una "caja negra" para iterar. Esto impedía la integración con la estructura de iteración del lenguaje y causaba errores.
Solución
Para que For Each funcione correctamente con tu colección, es necesario implementar la interfaz IEnumerable, y para soportar el trabajo con elementos tipados — IEnumerable(Of T). Debes crear también un enumerador propio (Enumerator) que implemente la interfaz IEnumerator (o IEnumerator(Of T) en versiones genéricas).
Ejemplo:
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 ' Uso: 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
Características clave:
¿Se puede implementar For Each sin la interfaz IEnumerable, simplemente escribiendo un método GetEnumerator?
No, Visual Basic requiere el soporte oficial de la interfaz IEnumerable/IEnumerable(Of T) para que el compilador reconozca la colección como compatible con For Each.
¿Debe el enumerador ser una clase separada?
No, si la base de datos de elementos soporta un enumerador estándar (por ejemplo, si agregas una List(Of T)), puedes devolver su GetEnumerator. Solo en escenarios complejos puede que se necesite una implementación propia de IEnumerator.
¿Se puede modificar la colección dentro del ciclo For Each?
No, modificar la colección durante la iteración provocará una InvalidOperationException. Se necesitan estrategias especiales para un manejo correcto (por ejemplo, copiar la lista antes de iterar o usar indexación).
En la empresa se desarrolló una clase MyCollection con adiciones y eliminaciones a través de un array, pero sin implementar la interfaz IEnumerable. For Each no funcionaba, teniendo que usar ciclos normales y un campo público con los elementos.
Pros:
Contras:
La clase fue reformulada, implementando IEnumerable(Of T). Después de eso, la colección se utilizó fácilmente y de manera estándar en For Each, además se volvió compatible con LINQ.
Pros:
Contras: