# Introduction

This article provides simple examples for working with Tuples. In the Microsoft documentation for the new tuple the code samples are written towards experienced developer while the novice developer will generally not grasp possibilities for using the new tuples.

`Public` `Function` ``` CouldHaveBeenAnonymousResults(pChar ````As` `Char````) ````As`
``` (Item ````As` ``` Char````, Occurrences ``As` `Integer````, Code ````As` ``` Integer````) `
`  Dim` `results =`
`  (`
```   From T ````In`
`   (`
```    From c ````In` ``` "T0*A1?0*23aTA3 4T4\+a4 ?407#?A*6T+"````.ToCharArray()`
```    Group c By c Into Group ````Select` ``` New``` `With`
`     {`
` ``     .Item = c,`
`      .Occurrences = Group.Count,`
`      .Code = Asc(c)`
`     }`
`   ).ToList.OrderBy(``Function``(x) x.Item)`
`  ).FirstOrDefault(``Function``(x) x.Item = pChar)`
`Return` `(results.Item, results.Occurrences, results.Code)`
`End` `Function`

# Definition of Tuple prior to Visual Studio 2017

A tuple is a data structure that has a specific number and sequence of elements. An example of a tuple is a data structure with three elements (known as a 3-tuple or triple) that is used to store an identifier such as a person's name in the first element, a year in the second element, and the person's income for that year in the third element. The .NET Framework directly supports tuples with one to seven elements. In addition, you can create tuples of eight or more elements by nesting tuple objects in the Rest property of a Tuple(Of T1, T2, T3, T4, T5, T6, T7, TRest) object.

Although tuples had a place in writing code the results sent back from a method returning a tuple were not readily understood as each tuple returns as (where "result" is the tuple) result.ItemN where N is a value such as first name. So if a method using a tuple returned first and last name we would have result.Item1 for first name and result.Item2 for last name.

An alternate would be to create a simple class with two properties, FirstName and LastName, create an instance of the class and return the values so we would have result.FirstName and result.LastName.

# Visual Studio 2017/VB.NET 15.4 Tuples

Visual Studio 2017, VB.NET 15.3 changed this by allowing a developer to create named values for tuples returning from a method call. When you instantiate the tuple, you define the number and the data type of each value (or element). For example, a 2-tuple (or pair) has two elements. For example, you have a strong typed list of Person class and want to return only first and last name you can write code as shown below.

# Basic Example

`Public` `Function` ``` FindPersonByIdentifierAsTuple(pIdentifier ````As` `Integer````) ````As` ``` (FirstName ````As` ``` String````, LastName ``As` `String``)`
` ``Dim` `personData = People.FirstOrDefault(``Function``(person) person.Identifier = pIdentifier)`

` ``If` `personData ``Is` `Nothing` ``` Then```
`  ``Return` `(``""````, ````""``)`
` ``Else`
`  ``Return` `(personData.FirstName, personData.LastName)`
` ``End` `If`

`End` `Function`

Which would be called as follows

`Dim` `identifier ``As` `Integer` ``` = 3```
`Dim` `ops = ``New` `Operations`
`Dim` `results = ops.FindPersonByIdentifierAsTuple(identifier)`

`If` `Not` ``` String````.IsNullOrWhiteSpace(results.FirstName) ` `Then`
` ``MessageBox.Show(\$``"{results.FirstName} {results.LastName} for id of {identifier}"``)`
`Else`
` ``MessageBox.Show(\$``"Failed to locate a person with the id of {identifier}"``)`
`End` `If`

The quark here for some is checking to see if results.FirstName is an empty string which indicates the passed identifier was not located. Some might consider simple returning a class instance as shown below which is perfectly okay to do yet what about if the class Person has many properties and moving parts were one example might be a Entity Framework entity such as Person with Account members? In this case the returning container of the instance Person is heavy rather than light weight as in the tuple example shown above.

`Public` `Function` ``` FindPersonByIdentifierAsPerson(pIdentifier ````As` `Integer````) ````As` ``` Person```
` ``Return` `People.FirstOrDefault(``Function``(person) person.Identifier = pIdentifier)`
`End` `Function`

# Anonymous type replacement

What is Anonymous type in VB.NET? An anonymous type is a class that contains one or more named values. Provides a quick and easy way to define simple classes for holding multiple values. Supports both mutable and immutable anonymous types.

A perfect example for using the new tuple is writing a LINQ or Lambda query which returns an anonymous type, using named tuples a developer can return two or more values from a method as shown below where a character is passed in, a Lambda statement finds the character and returns the character, Occurrences of that character and the code for the character.

`Public` `Function` ``` CouldHaveBeenAnonymousResults(pChar ````As` `Char````) ````As` ``` (Item ````As` ``` Char````, Occurrences ``As` `Integer````, Code ````As` ``` Integer````)`

` ``Dim` `results =`
`   ``(`
`    ````From T ````In`
`    ``(`
`     ````From c ````In` ``` "T0*A1?0*23aTA3 4T4\+a4 ?407#?A*6T+"````.ToCharArray()`
`     ````Group c By c Into Group ````Select` ``` New``` `With`
`     ``{`
`      ``.Item = c,`
`      ``.Occurrences = Group.Count,`
`      ``.Code = Asc(c)`
`     ``}`
`    ``).ToList.OrderBy(``Function``(x) x.Item)`
`   ``).FirstOrDefault(``Function``(x) x.Item = pChar)`

` ``Return` `(results.Item, results.Occurrences, results.Code)`

`End` `Function`

# Anonymous/Iterator/Yield example

An Anonymous function can be an iterator function and can return a tuple. In the following example a DataTable is loaded with mocked data which would represent data returned from reading data from a database table. The task is to find duplicates by name and return the primary key. In GetDuplicatesByIdentifier a Lambda statement goes a GroupBy to get the duplicates where the variable duplicates is IEnumberable(Of 'a') which is an anonymous type with three properties, in this case we will return name and identifier in the iterator function via Yield statement. In the calling method the name and identifier are placed into a second DataGridView to see the results. In a application the task might show this to the user and allow them to delete or not delete the duplicate rows.

`Public` `Class` ``` Form1```
` ``Private` `Sub` ``` Form1_Shown(sender ````As` `Object````, e ````As` ``` EventArgs) ````Handles` `Me``.Shown`
`  ``Dim` `dt = ``New` `DataTable ``With` `{.TableName = ``"MyTable"``}`

`  ``dt.Columns.Add(``New` `DataColumn ``With` `{.ColumnName = ``"Identifier"``,`
`       ````.DataType = ````GetType``(Int32),`
`       ````.AutoIncrement = ````True``, .AutoIncrementSeed = 1,`
`       ``.ColumnMapping = MappingType.Hidden})`

`  ``dt.Columns.Add(``New` `DataColumn ``With` `{.ColumnName = ``"Name"``,`
`       ````.DataType = ````GetType``(``String``)})`

`  ``dt.Columns.Add(``New` `DataColumn ``With` `{.ColumnName = ``"Status"``,`
`       ````.DataType = ````GetType``(``Boolean``)})`

`  ``dt.Rows.Add(``New` `Object``() {``Nothing````, ````"Karen"``, ` `False``})`
`  ``dt.Rows.Add(``New` `Object``() {``Nothing````, ````"Karen"``, ` `True``})`
`  ``dt.Rows.Add(``New` `Object``() {``Nothing````, ````"Bill"``, ``True``})`
`  ``dt.Rows.Add(``New` `Object``() {``Nothing````, ````"Karen"``, ` `False``})`
`  ``dt.Rows.Add(``New` `Object``() {``Nothing````, ````"Bill"``, ``True``})`

`  ``DataGridView1.DataSource = dt`

` ``End` `Sub`

` ``Private` `Sub` ``` cmdExecute_Click(sender ````As` `Object````, e ````As` ``` EventArgs) ````Handles` `cmdExecute.Click`

`  ``DataGridView2.Rows.Clear()`

`  ``For` `Each` ``` item ````In` ``` GetDuplicatesByIdendifier()```
`   ``DataGridView2.Rows.Add(item.Identifer, item.Name)`
`  ``Next`

` ``End` `Sub`
` ``Public` `Iterator ``Function` `GetDuplicatesByIdendifier() ``As` `IEnumerable(Of (Name ``As` `String````, Identifer ````As` ``` Integer````))`
`  ``Dim` `dt = ``CType``(DataGridView1.DataSource, DataTable)`

`  ``Dim` `duplicates = dt.AsEnumerable().`
`    ``GroupBy(``Function````(r) ````New` ``` With```
`       ``{`
`       ````Key .Name = ````CStr``(r(``"Name"``)),`
`       ``Key .Status = r(``"Status"``),`
`       ````.Identifier = ````CInt``(r(``"Identifier"``))`
`       ``}).`
`    ``Where(``Function``(gr) gr.Count() > 1).``Select``(``Function````(g) g.Key)```

`  ``For` `Each` ``` d ````In` ``` duplicates```
`   ``Yield (d.Name, d.Identifier)`
`  ``Next`
` ``End` `Function`

`End` `Class`

Another example using a iterator method using Entity Framework where a subset of data is needed. In this case the task is to return all countries with an identifier of 4 which is Brazil with the fields CustomerIdentifier (the primary key), CompanyName and ContactName where each field will have a different name then in the Entity model.

`Public` `Class` ``` Form1```
` ``Private` `Sub` ``` Button1_Click(sender ````As` `Object````, e ````As` ``` EventArgs) ````Handles` `Button1.Click`
`  ``Dim` `ops ``As` `New` ``` Operations```
`  ``For` `Each` ``` customer ````In` ``` ops.CustomersByCountryIdentifier(4)```
`   ``Console.WriteLine(\$``"Id: {customer.Identifer} Name: {customer.Name} Contact: {customer.ContactName}"``)`
`  ``Next`
` ``End` `Sub`
`End` `Class`
`Public` `Class` ``` Operations```
` ``Public` `Iterator ``Function` `CustomersByCountryIdentifier(`
`  ````pCountryIdentifier ````As` ``` Integer````) ``As` `IEnumerable(Of`
`  ````(Identifer ````As` ``` Integer````, Name ``As` `String````, ContactName ````As` ``` String````))`

`  ````Using context ````As` ``` New``` `NorthWindEntities`
`   ``Dim` `results = context.Customers.`
`    ``Where(``Function````(cust) cust.CountryIdentfier.HasValue ````And`
`    ``cust.CountryIdentfier.Value = pCountryIdentifier).`
`    ``Select``(``Function````(cust) ````New` ``` With```
`    ``{`
`     ``.Id = cust.CustomerIdentifier,`
`     ``.Company = cust.CompanyName,`
`     ``.Contact = cust.ContactName`
`    ``})`

`   ``For` `Each` ``` customer ````In` ``` results```
`    ``Yield (customer.Id, customer.Company, customer.Contact)`
`   ``Next`
`  ``End` `Using`
` ``End` `Function`
`End` `Class`

# Caveats

VB.NET tuples do not support discards as in C# 7.

# Summary

Using the new style tuple provides additional options for returning values from a function when more than one value needs to be returned and on the caller side the members of the tuple are easily understandable and strong typed.

# Source code

https://github.com/karenpayneoregon/VisualBasicNewTuples

In the source code there are three projects, one for demonstrating using a class instance to return data, the second for performing the same operation as the first project using named tuples while the third project demonstrates the alternate to working with anonymous types for dealing with returning information from a method. Note there are several classes that are there to show what could be returned if in the class example would be returned rather than using a light weight solution as with tuples.

# Requires

From NuGet package manager in your solution, add System.ValueTuple from the "Browse tab" or from NuGet console PM>Install-Package System.ValueTuple -Version 4.5.0