ProgrammingLibrary Developer .NET / Middle VB.NET Developer

Describe ways to implement and use iterators (the term Enumerable / Iterator) in Visual Basic, as well as how to create custom enumerable collections.

Pass interviews with Hintsage AI assistant

Answer.

Background
Iterators have become an important part of programming languages with the advent of collections that need to be iterated over. In Visual Basic, support for IEnumerable/IEnumerator has been available since the .NET version, allowing the implementation of custom enumerable collections and using the For Each loop for convenient and concise access to the contents of collections.

Problem
Many developers limit themselves to using only built-in collections or do not correctly implement the IEnumerable interface, resulting in the inability to iterate over objects using For Each. Additionally, implementing and maintaining the state of the iterator through Current and MoveNext can be challenging.

Solution
To support iterations, the type implements the IEnumerable interface (or the generic IEnumerable(Of T)). Implement the GetEnumerator function, returning an object that implements IEnumerator. In VB.NET, iterator methods with the Yield keyword can also be used (starting from VB 2015), which greatly simplifies the implementation of iteration.

Example code:

Imports System.Collections Public Class SimpleCollection Implements IEnumerable Private arr() As Integer = {1, 2, 3} Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator For Each i In arr Yield i ' for VB 2015+ Next End Function End Class ' Usage: For Each n As Integer In New SimpleCollection() Console.WriteLine(n) Next

Key features:

  • To support For Each, the type must implement IEnumerable / IEnumerable(Of T).
  • The implementation is usually delegated to an existing collection or a custom one is created.
  • With Yield, you can easily create iterators; without Yield, you will have to manually implement IEnumerator.

Trick Questions.

Is it mandatory to implement both interfaces: IEnumerable and IEnumerator, to support For Each?

No, it is sufficient to implement IEnumerable and return a compatible iterator. If you want to implement iteration, you can use an existing IEnumerator from an inner collection. However, for full customization, both parts need to be implemented.

Example code:

Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return arr.GetEnumerator() ' using the built-in array iterator End Function

Can IEnumerable be implemented only explicitly (Explicit Interface Implementation) in Visual Basic?

Yes, you can implement the interface explicitly, especially when your collection already has its own GetEnumerator and you want to hide the interface implementation. Such a GetEnumerator cannot be called directly, but For Each will work.

Example code:

Public Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator ' ...method body End Function

What happens if GetEnumerator returns Nothing?

An exception will be thrown at the first attempt to iterate over the collection, as For Each expects a valid iterator.

Common Errors and Anti-Patterns

  • Incorrect implementation of MoveNext/Current methods, causing elements to be skipped or errors to occur.
  • Violation of iterator state between calls (for example, not resetting the index when re-iterating).
  • Missing or incorrect StopIteration exception.

Real-life Example

Negative Case

The collection class did not implement IEnumerable, and a separate Next method was created for iterating over elements. As a result, it cannot be used in generic methods or For Each, requiring additional method implementations.

Pros:

  • Quick implementation tailored to the task.

Cons:

  • Cannot use For Each and LINQ.
  • Loss of compatibility with .NET API.

Positive Case

The developer implemented IEnumerable and GetEnumerator, the collection now works with For Each and integrates with LINQ.

Pros:

  • Super compatibility with .NET infrastructure.
  • Versatility and ease of use.

Cons:

  • Requires slightly more code for manual implementation before Yield was introduced.