Skip to content
AgileLogics
6 min read

Iterators, Extension Methods, and Operator Overloading

Prerequisites: LINQ

Iterators with Yield

An Iterator Function produces a sequence one item at a time with Yield. Items are computed only as they're requested:

Module Program
    Iterator Function Fibonacci() As IEnumerable(Of Long)
        Dim a = 0L, b = 1L
        Do
            Yield a
            Dim nextValue = a + b
            a = b
            b = nextValue
        Loop
    End Function

    Iterator Function Countdown(from As Integer) As IEnumerable(Of Integer)
        For i = from To 1 Step -1
            Console.WriteLine($"  (producing {i})")
            Yield i
        Next
    End Function

    Sub Main()
        Console.WriteLine(String.Join(" ", Fibonacci().Take(10)))
        Console.WriteLine(Fibonacci().First(Function(n) n > 1000))
        For Each n In Countdown(2)
            Console.WriteLine(n)
        Next
    End Sub
End Module
0 1 1 2 3 5 8 13 21 34
1597
  (producing 2)
2
  (producing 1)
1

Fibonacci loops forever, but Take(10) only asks for ten values. The "producing" lines show the lazy, step-by-step evaluation.

Extension methods

An extension method adds a method to an existing type — even one you didn't write, like String. It lives in a module and is marked <Extension>:

Imports System.Runtime.CompilerServices

Module StringExtensions
    <Extension>
    Public Function Shout(text As String) As String
        Return text.ToUpper() & "!"
    End Function

    <Extension>
    Public Function WordCount(text As String) As Integer
        Return text.Split({" "c}, StringSplitOptions.RemoveEmptyEntries).Length
    End Function
End Module

Module Program
    Sub Main()
        Console.WriteLine("hello".Shout())
        Console.WriteLine("the quick  brown fox".WordCount())
    End Sub
End Module
HELLO!
4

The first parameter is the value the method is called on. LINQ's Where, Select, and Sum are extension methods on IEnumerable(Of T).

Operator overloading

Public Structure Money
    Public ReadOnly Property Amount As Decimal
    Public ReadOnly Property Currency As String

    Public Sub New(amount As Decimal, currency As String)
        Me.Amount = amount
        Me.Currency = currency
    End Sub

    Public Shared Operator +(a As Money, b As Money) As Money
        If a.Currency <> b.Currency Then Throw New InvalidOperationException("Currency mismatch.")
        Return New Money(a.Amount + b.Amount, a.Currency)
    End Operator

    Public Shared Operator *(a As Money, factor As Decimal) As Money
        Return New Money(a.Amount * factor, a.Currency)
    End Operator

    Public Shared Operator =(a As Money, b As Money) As Boolean
        Return a.Amount = b.Amount AndAlso a.Currency = b.Currency
    End Operator

    Public Shared Operator <>(a As Money, b As Money) As Boolean
        Return Not (a = b)
    End Operator

    Public Overrides Function ToString() As String
        Return $"{Amount:F2} {Currency}"
    End Function
End Structure

Module Program
    Sub Main()
        Dim price As New Money(19.99D, "USD")
        Dim total = price * 3 + New Money(5D, "USD")
        Console.WriteLine(total)
        Console.WriteLine(total = New Money(64.97D, "USD"))
    End Sub
End Module
64.97 USD
True

Operators are Shared Operator members. Some must come in pairs: = with <>, < with >. When you overload =, also override Equals and GetHashCode, so that collections and LINQ agree with your operator — the VB compiler doesn't remind you.