Generics Package
Author: mansellan.
The Generics contributed package provides seven tuple classes, TupleOf1 to TupleOf7. A tuple is a read-only object that holds one to seven values, called its items, each of a type given when the tuple is declared.
A tuple lets a function return several values at once, without a user-defined type declared for the purpose or ByRef parameters for the results. Each class is a generic class: TupleOf2(Of Long, String) holds a Long and a String, and its Item1 and Item2 properties return them with those types. The classes follow .NET’s System.Tuple classes, and their source comments are taken from .NET’s, under the MIT licence. This section documents version 1.0.0.0, published under the MIT licence.
Dim t As TupleOf3(Of String, Long, Date) = New TupleOf3(Of String, Long, Date)("Ada", 36, #12/10/1815#)
Debug.Print t.Item1 ' Ada
Debug.Print t.Item2 ' 36
Debug.Print Year(t.Item3) ' 1815
Syntax
New TupleOfn (Of T1 [ , T2 … ] ) ( item1 [ , item2 … ] )
tuple.Itemk
- n
- The number of items, 1 to 7. Each number has a class of its own, from TupleOf1 to TupleOf7.
- T1, T2 …
- The types of the items, one for each item, in order. All n must be given. Types of the items lists the types that work.
- item1, item2 …
- required The values of the items, one for each type, in the same order. The parameters are ByRef; see Creating a tuple.
- tuple
- An object expression that evaluates to a tuple.
- k
- The number of an item, 1 to n.
Itemk returns the kth item, of type Tk. The properties are read-only: an item is set only when the tuple is created, and nothing changes it afterwards. The classes have no other members.
| Class | Type parameters | Properties |
|---|---|---|
| TupleOf1 | T1 | Item1 |
| TupleOf2 | T1, T2 | Item1, Item2 |
| TupleOf3 | T1 to T3 | Item1 to Item3 |
| TupleOf4 | cannot be used; see TupleOf4 | |
| TupleOf5 | T1 to T5 | Item1 to Item5 |
| TupleOf6 | T1 to T6 | Item1 to Item6 |
| TupleOf7 | T1 to T7 | Item1 to Item7 |
There is no class for eight or more items. The package’s source holds one, TupleOfN, as comments only.
Creating a tuple
New creates a tuple and passes the items to the class’s constructor. The As New form of Dim takes no arguments, so a tuple is created with New and assigned to a variable declared with the tuple’s type:
Dim size As TupleOf2(Of Long, Long)
Set size = New TupleOf2(Of Long, Long)(640, 480)
Debug.Print size.Item1 * size.Item2 ' 307200
A function that returns a tuple declares the tuple’s type as its return type:
Module GenericsMinMaxDemo
' Returns the smallest and the largest value of an array.
Public Function MinMax(values() As Double) As TupleOf2(Of Double, Double)
Dim lo As Double = values(LBound(values))
Dim hi As Double = lo
Dim i As Long
For i = LBound(values) + 1 To UBound(values)
If values(i) < lo Then lo = values(i)
If values(i) > hi Then hi = values(i)
Next
Return New TupleOf2(Of Double, Double)(lo, hi)
End Function
Public Sub Report()
Dim data(2) As Double
data(0) = 4.5
data(1) = -2
data(2) = 9
Dim r As TupleOf2(Of Double, Double) = MinMax(data)
Debug.Print "Smallest:"; r.Item1; " Largest:"; r.Item2
End Sub
End Module
Note
In BETA 1005, New with a generic class from a package compiles only when some declaration in the project or the package names the same class with the same type arguments. A variable declared with the tuple’s type is such a declaration. Without one, Dim t As Any = New TupleOf2(Of String, Long)("a", 2) fails with TB5074, Could not bind to parameterized constructor of class ‘TupleOf2’, and so does New assigned to an Object or Variant variable or passed as an argument. A generic class declared in the project’s own code is not affected. Assign the tuple to a variable of its own type first; that variable can then be assigned to an Object or Variant variable, through which the items are read late-bound:
Dim t As TupleOf2(Of String, Long) = New TupleOf2(Of String, Long)("width", 640)
Dim o As Object
Set o = t
Debug.Print o.Item1; o.Item2 ' width 640
The constructor’s parameters are declared without ByVal, so they are ByRef. A variable passed for an item must have the item’s type exactly: a Byte variable passed for a Long item fails with TB5001, cannot coerce type ‘Byte’ to [ByRef] ‘Long’. A literal or any other expression is converted as usual, so wrap such a variable in a conversion function. The tuple keeps a copy of each value, and a later change to the variable does not change the tuple:
Dim b As Byte = 200
Dim t As TupleOf1(Of Long) = New TupleOf1(Of Long)(CLng(b))
b = 1
Debug.Print t.Item1 ' 200
Reading the items
Each item is read through its Item property, which returns it with the type it was declared with. A tuple is an object, so it is assigned with Set, and a variable that has not been assigned one is Nothing. Two tuples with equal items are still two objects: Is compares the objects themselves, so the items are compared one by one. TypeName returns the class’s name without its type arguments:
Dim a As TupleOf2(Of Long, String) = New TupleOf2(Of Long, String)(1, "x")
Dim b As TupleOf2(Of Long, String) = New TupleOf2(Of Long, String)(1, "x")
Debug.Print TypeName(a) ' TupleOf2
Debug.Print a Is b ' False
Debug.Print a.Item1 = b.Item1 And a.Item2 = b.Item2 ' True
Unlike .NET’s tuples, the classes have no method that compares two tuples or converts one to a string.
Types of the items
An item can have any numeric type, String, Boolean, Date, Currency or LongPtr. Three kinds of type cannot be used:
- Variant. twinBASIC refuses Variant as a type argument: TB5124, Cannot pass Variant datatype as a generic specifier, use a more explicit type instead.
- Object types. The constructor stores each item with
=, not with Set. A class whose default member takes an argument, such as Collection, fails to compile, with TB5092 Missing argument ‘Index’ reported in the package’s source. Object compiles, but creating the tuple raises run-time error 91, Object variable or With block variable not set, even when the item passed is Nothing. So a tuple cannot hold another tuple either. - Arrays, such as Long().
Warning
In BETA 1005, a tuple type with an array as a type argument, such as TupleOf1(Of Long()), crashes the compiler wherever it is written: in a declaration, a parameter, a function’s result type or after New. The compiler restarts and crashes again, so nothing in the project compiles until the line is removed. The same happens with a generic class declared in a project’s own code.
TupleOf4
Note
TupleOf4 cannot be used in version 1.0.0.0. The package declares it with three type parameters, TupleOf4(Of T1, T2, T3), but its fourth item uses a fourth, T4, which is not declared. With four type arguments, a declaration fails with TB5124, Too many generic specifiers. With three, the package’s source fails with TB5079, Unrecognized datatype symbol ‘T4’. A project that does not use TupleOf4 is not affected. For four values, use TupleOf5 and leave its last item unused.
See Also
- Generics – generic classes, procedures and types in twinBASIC
- Parameterized class constructors – the Sub New that each tuple class declares
- New operator – creates an object, passing arguments to its constructor