Skip to content
AgileLogics
6 min read

Lambdas and Delegates

Prerequisites: Generics

A delegate is a type for "a reference to a procedure". A lambda expression creates one inline.

Lambda expressions

Module Program
    Sub Main()
        Dim square As Func(Of Integer, Integer) = Function(x) x * x
        Dim add = Function(a As Integer, b As Integer) a + b
        Dim greet As Action(Of String) = Sub(name) Console.WriteLine($"Hi, {name}")

        Console.WriteLine(square(7))
        Console.WriteLine(add(2, 3))
        greet("Ada")

        Dim describe = Function(n As Integer) As String
                           If n Mod 2 = 0 Then Return "even"
                           Return "odd"
                       End Function
        Console.WriteLine(describe(7))
    End Sub
End Module
49
5
Hi, Ada
odd
  • Function(x) expression returns a value; Sub(x) statement doesn't.
  • Multi-line lambdas end with End Function or End Sub.
  • Func(Of T1, ..., TResult) and Action(Of T1, ...) are the built-in delegate types for functions and subs.

Passing functions to functions

Module Program
    Function ApplyToAll(values As Integer(), f As Func(Of Integer, Integer)) As Integer()
        Dim result(values.Length - 1) As Integer
        For i = 0 To values.Length - 1
            result(i) = f(values(i))
        Next
        Return result
    End Function

    Function Triple(x As Integer) As Integer
        Return x * 3
    End Function

    Sub Main()
        Dim nums = {1, 2, 3}
        Console.WriteLine(String.Join(" ", ApplyToAll(nums, Function(x) x + 10)))
        Console.WriteLine(String.Join(" ", ApplyToAll(nums, AddressOf Triple)))

        Dim words As New List(Of String) From {"ccc", "a", "bb"}
        words.Sort(Function(x, y) x.Length.CompareTo(y.Length))
        Console.WriteLine(String.Join(" ", words))
        Console.WriteLine(words.Find(Function(w) w.StartsWith("b")))
    End Sub
End Module
11 12 13
3 6 9
a bb ccc
bb

AddressOf Triple turns a named procedure into a delegate.

Closures

A lambda can capture local variables around it:

Module Program
    Function MakeCounter() As Func(Of Integer)
        Dim count = 0
        Return Function()
                   count += 1
                   Return count
               End Function
    End Function

    Sub Main()
        Dim c1 = MakeCounter()
        Dim c2 = MakeCounter()
        Console.WriteLine($"{c1()} {c1()} {c1()} {c2()}")
    End Sub
End Module
1 2 3 1

Each counter keeps its own count alive after MakeCounter returns.

Custom delegate types

Public Delegate Function Discount(price As Decimal) As Decimal

Module Program
    Sub Main()
        Dim rules As Discount() = {
            Function(p) p * 0.9D,
            Function(p) Math.Max(0D, p - 5D)
        }
        For Each rule In rules
            Console.WriteLine(rule(30D))
        Next
    End Sub
End Module
27.0
25

Named delegate types document intent, but Func and Action cover most needs. Delegates are also the foundation of events (later in this level).