Visual Studio 2017
Use ValueTuples in Visual Basic .NET

Visual Studio 2017
Use ValueTuples in Visual Basic .NET

Tuples are primitive types where you can easily combine multiple values to one variable. They are as a datatype not completely new for C# 7.0 or Visual Basic 15, but the way you can use them is, with the new System.ValueTuple type, much improved. In this post I will show you how you can take advantage of the streamed syntax of value tuples. The old System.Tuple became not very populair, mainly because the results where not strongly named; you have to use them like result.Item1, result.Item2 etc.

Whenever you need to return your function more than one return value, you have to decide what to do. Are you passing a couple of ByRef-arguments in your method declaration or are you defining a specific ‘result’ class; with for each return value a public property? For many years I’m not a big fan of using ByRef parameters. It makes my code harder to read, debug and understand. Mostly due the fact that you have to declare the parameters first, before you can use them. So, I’m the type of guy who creates for all those cases a separate class. But for returning a couple of primitives, or better said, value types that’s quite expensive. In runtime, there will be for every method call a new object created, which should be managed accordingly.

Consider the following example. I want to calculate an average, but I need also the count of the values where the average is based on. This value is also ‘defined’ in my very complicated calculation algorithm. Let’s do it the old way:

Sub Main()
    Dim numbers As New List(Of Integer) From {3, 4, 5}
    Dim count As Integer
    Dim averageNumber = CalculateAverage(numbers, count)
    Debug.WriteLine($"Average: {averageNumber}, Count: {count}")
End Sub

Function CalculateAverage(numbers As List(Of Integer), ByRef count As Integer)
                                                                         As Double
    count = numbers.Count
    Return numbers.Sum() / count
End Function

To avoid the ByRef-construct, I preferred to define a CalculateAverageResult class, which is used to return multiple values.

Class CalculateAverageResult
    Public Property Average As Double
    Public Property Count As Integer
End Class

Sub Main()
    Dim numbers As New List(Of Integer) From {3, 4, 5}
    Dim result = CalculateAverage(numbers)
    Debug.WriteLine($"Average: {result.Average}, Count: {result.Count}")
End Sub

Function CalculateAverage(numbers As List(Of Integer)) As CalculateAverageResult
    Dim average = numbers.Sum() / numbers.Count
    Return New CalculateAverageResult() With {.Average = average, 
                                               .Count = numbers.Count}
End Function

But, beside the fact that you have to define an extra class, this gives also a bit overhead in your code when executed. It becomes a reference type which lives on the heap and garbage collection should do it’s work. Let’s take a look how you can accomplish the same with the new System.ValueTuple datatype. To use this type, add the System.ValueTuple NuGet package to your project.
Calling my CalculateAverage() method stays actually the same, but we don’t need to declare an extra class and we can stay with only value types, which – as already stated – are much faster to process.

System.ValueTuple in Visual Basic
System.ValueTuple in Visual Basic
Sub Main()
    Dim numbers As New List(Of Integer) From {3, 4, 5}
    Dim result = CalculateAverage(numbers)
    Debug.WriteLine($"Average: {result.Average}, Count: {result.Count}")
End Sub

Function CalculateAverage(numbers As List(Of Integer)) As 
                                             (Average As Double, Count As Integer)
    Return (numbers.Sum() / numbers.Count, numbers.Count)
End Function

As you can see in the screenshot, the return type defines two variables, but for your purpose you can add easily some more. It makes your code more easily to maintain.  You can use value tuples also in combination in Async and Await scenarios:

Async Function CalculateAverageAsync(numbers As List(Of Integer)) As 
                                          Task(Of (Average As Double, Count As Integer))
    Return Await Task.FromResult((numbers.Sum() / numbers.Count, numbers.Count))
End Function

You can use this type also as argument in a method declaration, so not only as a return type. Using it this way you can group variables together, which is more clean when consuming the actual values or when passing the variable to another method.

Sub Main()
    Dim person = ("André", New Date(1969, 5, 6))
    PrintPerson(person)
End Sub

Sub PrintPerson(person As (firstName As String, birthDate As Date))
    Debug.WriteLine($"{person.firstName} - {person.birthDate.ToString()}")
End Sub

To let this code running, you need Visual Studio 2017. You can add a reference to the NuGet package in previous versions of Visual Studio, but their compilers will not understand these new language/syntax. You can download the new version from here.

Leave a Reply