Since I'm using the partitions locally, I ended up chosing to invert the situation: instead of passing the partitions to the action using it, I can pass the action to the function doing the partitioning.
Public Sub DoForPartition(Of T)(source As IEnumerable(Of T),
size As Integer,
doThis As Action(Of IEnumerable(Of T)))
Dim partition(size - 1) As T
Dim count = 0
For Each t in source
partition(count) = t
count += 1
If count = size Then
doThis(partition)
count = 0
End If
Next
If count > 0 Then
Array.Resize(partition, count)
doThis(partition)
End If
End Sub
This function avoids looping through the source multiple times and the only memory overhead is the size of the partition (instead of the entire source like some of the other options). I didn't write this function myself, but adapted a similarly looking C# function from this answer.
This looks like a much better algorithm than the one in my question.