Tupel (Visual Basic)
Ab Visual Basic 2017 bietet die Visual Basic-Sprache integrierte Unterstützung für Tupel, was die Erstellung von Tupeln und den Zugriff auf die Elemente von Tupeln erleichtert. Ein Tupel ist eine einfache Datenstruktur, die über eine bestimmte Anzahl und Sequenz von Werten verfügt. Beim Instanziieren des Tupels werden Anzahl und Datentyp jedes Werts (oder Elements) definiert. Ein 2-Tupel (oder Paar) hat zum Beispiel zwei Elemente. Das erste ist vielleicht ein Boolean
-Wert und das zweite ein String
. Da Tupel die unkomplizierte Speicherung mehrerer Werte in einem einzelnen Objekt ermöglichen, werden sie häufig verwendet, um auf einfache Weise mehrere Werte aus einer Methode zurückzugeben.
Wichtig
Für die Tupelunterstützung ist der Typ ValueTuple erforderlich. Falls .NET Framework 4.7 nicht installiert ist, muss das im NuGet-Katalog verfügbare NuGet-Paket System.ValueTuple
hinzugefügt werden. Ohne dieses Paket tritt möglicherweise ein Kompilierungsfehler auf, in dem darauf hingewiesen wird, dass der vordefinierte Typ „ValueTuple(Of,,,)“ nicht definiert oder importiert wurde.
Instanziieren und Verwenden eines Tupels
Ein Tupel wird instanziiert, indem seine durch Kommas getrennten Werte in eine Klammer eingeschlossen werden. Jeder dieser Werte wird dann zu einem Feld des Tupels. Der folgende Code definiert z. B. ein Tripel (oder 3-Tupel) mit einem Date
-, einem String
- und einem Boolean
-Wert (in dieser Reihenfolge).
Dim holiday = (#07/04/2017#, "Independence Day", True)
Standardmäßig besteht der Name jedes Felds in einem Tupel aus der Zeichenfolge Item
, gefolgt von der auf 1 basierenden Position des Felds im Tupel. Bei diesem 3-Tupel ist das Date
-Feld Item1
, das String
-Feld ist Item2
, und das Boolean
-Feld ist Item3
. Im folgenden Beispiel werden die Werte der Felder des Tupels angezeigt, das in der vorherigen Codezeile instanziiert wurde:
Console.WriteLine($"{holiday.Item1} is {holiday.Item2}" +
$"{If(holiday.Item3, ", a national holiday", String.Empty)}")
' Output: 7/4/2017 12:00:00 AM Is Independence Day, a national holiday
Die Felder eines Visual Basic-Tupels sind les- und schreibbar, und die Werte eines Tupels können nach dem Instanziieren geändert werden. Im folgenden Beispiel werden zwei der drei Felder des im vorherigen Beispiel erstellten Tupels geändert, und das Ergebnis wird angezeigt:
holiday.Item1 = #01/01/2018#
holiday.Item2 = "New Year's Day"
Console.WriteLine($"{holiday.Item1} is {holiday.Item2}" +
$"{If(holiday.Item3, ", a national holiday", String.Empty)}")
' Output: 1/1/2018 12:00:00 AM Is New Year's Day, a national holiday
Instanziieren und Verwenden eines benannten Tupels
Anstatt Standardnamen für die Felder eines Tupels zu verwenden, können Sie auch ein benanntes Tupel instanziieren, indem Sie den Elementen des Tupels benutzerdefinierte Namen zuweisen. Auf die Felder des Tupels kann dann über die zugewiesenen Namen oder über die Standardnamen zugegriffen werden. Im folgenden Beispiel wird das gleiche 3-Tupel wie zuvor instanziiert. Diesmal werden die Felder aber explizit benannt. Das erste Feld erhält den Namen EventDate
, das zweite den Namen Name
und das dritte den Namen IsHoliday
. Anschließend werden die Feldwerte angezeigt, geändert und die Feldwerte erneut angezeigt.
Dim holiday = (EventDate:=#07/04/2017#, Name:="Independence Day", IsHoliday:=True)
Console.WriteLine($"{holiday.EventDate} Is {holiday.Name}" +
$"{If(holiday.IsHoliday, ", a national holiday", String.Empty)}")
holiday.Item1 = #01/01/2018#
holiday.Item2 = "New Year's Day"
Console.WriteLine($"{holiday.Item1} is {holiday.Item2}" +
$"{If(holiday.Item3, ", a national holiday", String.Empty)}")
' The example displays the following output:
' 7/4/2017 12:00:00 AM Is Independence Day, a national holiday
' 1/1/2018 12:00:00 AM Is New Year's Day, a national holiday
Sie können die Tupelnamen auch als Teil der Typdeklaration einer Variablen, eines Felds oder eines Parameters angeben:
Dim holiday As (EventDate As Date, Name As String, IsHoliday As Boolean) =
(#07/04/2017#, "Independence Day", True)
Console.WriteLine(holiday.Name)
' Output: Independence Day
Eine weitere Möglichkeit ist die Angabe im Rückgabetyp einer Methode.
Dies ist besonders nützlich, wenn Tupel für einen Auflistungsinitialisierer bereitgestellt werden. Die Tupelnamen können als Teil der Typdeklaration der Auflistung bereitgestellt werden:
Dim events As New List(Of (EventDate As Date, Name As String, IsHoliday As Boolean)) From {
(#07/04/2017#, "Independence Day", True),
(#04/22/2017#, "Earth Day", False)
}
Console.WriteLine(events(1).IsHoliday)
' Output: False
Abgeleitete Tupelelementnamen
Ab Visual Basic 15.3 kann Visual Basic die Namen von Tupelelementen ableiten, sodass sie nicht explizit zugewiesen werden müssen. Abgeleitete Tupelnamen sind nützlich, wenn Sie ein Tupel über eine Gruppe von Variablen initialisieren und der Name des Tupelelements dem Variablennamen entsprechen soll.
Im folgenden Beispiel wird ein stateInfo
-Tupel erstellt, das drei explizit benannte Elemente enthält: state
, stateName
und capital
. Beachten Sie, dass die Tupelinitialisierungsanweisung aufgrund der Benennung der Elemente den benannten Elementen einfach die Werte der identisch benannten Variablen zuweist.
Const state As String = "MI"
Const stateName As String = "Michigan"
Const capital As String = "Lansing"
Dim stateInfo = (state:=state, stateName:=stateName, capital:=capital)
Console.WriteLine($"{stateInfo.stateName}: 2-letter code: {stateInfo.state}, Capital {stateInfo.capital}")
' The example displays the following output:
' Michigan: 2-letter code: MI, Capital Lansing
Da Elemente und Variablen den gleichen Namen haben, kann der Visual Basic-Compiler die Namen der Felder ableiten, wie im folgenden Beispiel gezeigt:
Const state As String = "MI"
Const stateName As String = "Michigan"
Const capital As String = "Lansing"
Dim stateInfo = (state, stateName, capital)
Console.WriteLine($"{stateInfo.stateName}: 2-letter code: {stateInfo.State}, Capital {stateInfo.capital}")
' The example displays the following output:
' Michigan: 2-letter code: MI, Capital Lansing
Um abgeleitete Namen von Tupelelementen zu aktivieren, müssen Sie in Ihrer Visual Basic-Projektdatei (VBPROJ-Datei) die zu verwendende Version des Visual Basic-Compilers definieren:
<PropertyGroup>
<LangVersion>15.3</LangVersion>
</PropertyGroup>
Die Versionsnummer kann eine beliebige Version des Visual Basic-Compilers ab der Version 15.3 sein. Anstatt eine bestimmte Compilerversion hart zu codieren, können Sie auch „Latest“ als Wert von LangVersion
angeben, um für die Kompilierung die neueste Version des auf Ihrem System installierten Visual Basic-Compilers zu verwenden.
Weitere Informationen finden Sie unter Select the Visual Basic language version (Auswählen der Visual Basic-Sprachversion).
Manchmal kann der Visual Basic-Compiler den Namen eines Tupelelements nicht vom Kandidatennamen ableiten. In diesen Fällen kann nur über den Standardnamen (Item1
, Item2
usw.) auf das Tupelfeld verwiesen werden. Dies ist in folgenden Szenarien der Fall:
Der Kandidatenname entspricht dem Namen eines Tupelelements (beispielsweise
Item3
,Rest
oderToString
).Der Kandidatenname wird im Tupel dupliziert.
Im Falle eines nicht erfolgreichen Feldnamenrückschlusses generiert Visual Basic keinen Compilerfehler, und es wird auch keine Ausnahme zur Laufzeit ausgelöst. Stattdessen muss auf Tupelfelder über den jeweils vordefinierten Namen wie Item1
und Item2
verwiesen werden.
Tupel im Vergleich zu Strukturen
Ein Visual Basic-Tupel ist ein Werttyp, bei dem es sich um eine Instanz eines der generischen System.ValueTuple-Typen handelt. Das im vorherigen Beispiel definierte Tupel holiday
ist beispielsweise eine Instanz der Struktur ValueTuple<T1,T2,T3>. Es ist als einfacher Container für Daten konzipiert. Da das Tupel dazu dient, die einfache Erstellung eines Objekts mit mehreren Datenelementen zu ermöglichen, fehlen ihm einige der Features, die ggf. bei einer benutzerdefinierten Struktur zur Verfügung stehen. Dazu gehören:
Benutzerdefinierte Member. Für ein Tupel können keine eigenen Eigenschaften, Methoden oder Ereignisse definiert werden.
Validierung. Die den Feldern zugewiesenen Daten können nicht überprüft werden.
Unveränderlichkeit. Visual Basic-Tupel sind änderbar. Im Gegensatz dazu können Sie bei einer benutzerdefinierten Struktur steuern, ob eine Instanz änderbar oder unveränderlich sein soll.
Wenn Sie benutzerdefinierte Member, Eigenschafts- und Feldvalidierung oder Unveränderlichkeit benötigen, sollten Sie die Structure-Anweisung von Visual Basic verwenden, um einen benutzerdefinierten Werttyp zu definieren.
Ein Visual Basic-Tupel erbt die Member des zugehörigen ValueTuple-Typs. Hierzu zählen neben den zugehörigen Feldern die folgenden Methoden:
Methode | BESCHREIBUNG |
---|---|
CompareTo | Vergleicht das aktuelle Tupel mit einem anderen Tupel mit der gleichen Anzahl von Elementen. |
Equals | Überprüft, ob das aktuelle Tupel einem anderen Tupel oder Objekt entspricht. |
GetHashCode | Berechnet den Hashcode für die aktuelle Instanz. |
ToString | Gibt die Zeichenfolgendarstellung dieses Tupels im Format (Item1, Item2...) zurück, wobei Item1 und Item2 die Werte der Felder des Tupels darstellen. |
Darüber hinaus werden von den ValueTuple-Typen Schnittstellen vom Typ IStructuralComparable und IStructuralEquatable implementiert, mit denen Sie benutzerdefinierte Vergleiche definieren können.
Zuweisung und Tupel
Visual Basic unterstützt die Zuweisung zwischen Tupeltypen, die über die gleiche Anzahl von Feldern verfügen. Die Feldtypen können konvertiert werden, wenn einer der folgenden Punkte zutrifft:
Quell- und Zielfeld sind vom gleichen Typ.
Eine erweiternde (oder implizite) Konvertierung des Quelltyps in den Zieltyp wurde definiert.
Option Strict
istOn
, und eine einschränkende (oder explizite) Konvertierung des Quelltyps in den Zieltyp wurde definiert. Bei dieser Konvertierung kann eine Ausnahme auftreten, wenn der Quellwert außerhalb des Bereichs des Zieltyps liegt.
Andere Konvertierungen gelten nicht für Zuordnungen. Sehen wir uns die Arten von Zuweisungen an, die zwischen Tupeltypen zulässig sind.
Berücksichtigen Sie diese Variablen, die in den folgenden Beispielen verwendet werden:
' The number and field types of all these tuples are compatible.
' The only difference Is the field names being used.
Dim unnamed = (42, "The meaning of life")
Dim anonymous = (16, "a perfect square")
Dim named = (Answer:=42, Message:="The meaning of life")
Dim differentNamed = (SecretConstant:=42, Label:="The meaning of life")
Bei den ersten beiden Variablen (unnamed
und anonymous
) wurden keine semantischen Namen für die Felder bereitgestellt. Die Felder haben die Standardnamen Item1
und Item2
. Die letzten beiden Variablen (named
und differentName
) verfügen über semantische Feldnamen. Beachten Sie, dass diese zwei Tupel unterschiedliche Namen für die Felder besitzen.
Alle vier dieser Tupel haben die gleiche Anzahl von Feldern (was als Arität bezeichnet wird), und die Typen dieser Felder sind identisch. Daher funktionieren alle Zuweisungen:
' Assign named to unnamed.
named = unnamed
' Despite the assignment, named still has fields that can be referred to as 'answer' and 'message'.
Console.WriteLine($"{named.Answer}, {named.Message}")
' Output: 42, The meaning of life
' Assign unnamed to anonymous.
anonymous = unnamed
' Because of the assignment, the value of the elements of anonymous changed.
Console.WriteLine($"{anonymous.Item1}, {anonymous.Item2}")
' Output: 42, The meaning of life
' Assign one named tuple to the other.
named = differentNamed
' The field names are Not assigned. 'named' still has 'answer' and 'message' fields.
Console.WriteLine($"{named.Answer}, {named.Message}")
' Output: 42, The meaning of life
Beachten Sie, dass die Namen der Tupel nicht zugewiesen sind. Die Werte der Felder werden nach der Reihenfolge der Felder im Tupel zugewiesen.
Beachten Sie außerdem, dass das Tupel named
dem Tupel conversion
zugewiesen werden kann, obwohl das erste Feld von named
den Typ Integer
und das erste Feld von conversion
den Typ Long
hat. Diese Zuweisung ist erfolgreich, da es sich bei der Konvertierung von Integer
in Long
um eine erweiternde Konvertierung handelt.
' Assign an (Integer, String) tuple to a (Long, String) tuple (using implicit conversion).
Dim conversion As (Long, String) = named
Console.WriteLine($"{conversion.Item1} ({conversion.Item1.GetType().Name}), " +
$"{conversion.Item2} ({conversion.Item2.GetType().Name})")
' Output: 42 (Int64), The meaning of life (String)
Tupel mit unterschiedlicher Anzahl von Feldern sind nicht zuweisbar:
' Does not compile.
' VB30311: Value of type '(Integer, Integer, Integer)' cannot be converted
' to '(Answer As Integer, Message As String)'
var differentShape = (1, 2, 3)
named = differentShape
Tupel als Methodenrückgabewert
Eine Methode kann nur einen einzelnen Wert zurückgeben. Oftmals sollen bei einem Methodenaufruf jedoch mehrere Werte zurückgegeben werden. Es gibt mehrere Möglichkeiten, diese Einschränkung zu umgehen:
Sie können eine benutzerdefinierte Klasse oder Struktur erstellen, deren Eigenschaften oder Felder Werte darstellen, die von der Methode zurückgegeben werden. Diese Lösung ist umständlich, da hierzu ein benutzerdefinierter Typ erstellt werden muss, dessen einziger Zweck darin besteht, Werte aus einem Methodenaufruf abzurufen.
Sie können einen einzelnen Wert von der Methode zurückgeben und die restlichen Werte zurückgeben, indem Sie sie durch Verweis auf die Methode übergeben. Hierzu muss zusätzlich eine Variable instanziiert werden, und es besteht die Gefahr, dass der Wert der Variablen, die Sie durch Verweis übergeben, versehentlich überschrieben wird.
Sie können ein Tupel als einfache Lösung zum Abrufen mehrerer Rückgabewerte verwenden.
Ein Beispiel: Die Methoden vom Typ TryParse in .NET geben einen Boolean
-Wert zurück, der angibt, ob der Analysevorgang erfolgreich war. Das Ergebnis des Analysevorgangs wird in einer Variablen zurückgegeben, die durch Verweis an die Methode übergeben wird. In der Regel sieht der Aufruf einer Analysemethode wie Integer.TryParse wie folgt aus:
Dim numericString As String = "123456"
Dim number As Integer
Dim result = Integer.TryParse(numericString, number)
Console.WriteLine($"{If(result, $"Success: {number:N0}", "Failure")}")
' Output: Success: 123,456
Wir können ein Tupel aus dem Analysevorgang zurückgeben, wenn wir den Aufruf der Methode Integer.TryParse in unserer eigenen Methode verpacken. Im folgenden Beispiel NumericLibrary.ParseInteger
wird die Methode Integer.TryParse aufgerufen. Anschließend wird ein benanntes Tupel mit zwei Elementen zurückgegeben.
Imports System.Globalization
Public Module NumericLibrary
Public Function ParseInteger(value As String) As (Success As Boolean, Number As Integer)
Dim number As Integer
Return (Integer.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, number), number)
End Function
End Module
Anschließend können Sie die Methode mit Code wie dem folgenden aufrufen:
Dim numericString As String = "123,456"
Dim result = ParseInteger(numericString)
Console.WriteLine($"{If(result.Success, $"Success: {result.Number:N0}", "Failure")}")
Console.ReadLine()
' Output: Success: 123,456
Visual Basic-Tupel und Tupel in .NET Framework
Ein Visual Basic-Tupel ist eine Instanz eines der generischen System.ValueTuple-Typen, die in .NET Framework 4.7 eingeführt wurden. .NET Framework enthält auch eine Reihe generischer System.Tuple-Klassen. Diese Klassen unterscheiden sich jedoch in einigen Punkten von Visual Basic-Tupeln und den generischen System.ValueTuple-Typen:
Die Elemente der Klassen vom Typ Tuple sind Eigenschaften mit den Namen
Item1
,Item2
usw. In Visual Basic-Tupeln und den ValueTuple-Typen sind Tupelelemente Felder.Den Elementen einer Instanz von Tuple oder ValueTuple können keine aussagekräftigen Namen zugewiesen werden. In Visual Basic können Sie Namen zuweisen, die Aufschluss über die Bedeutung der Felder geben.
Die Eigenschaften einer Instanz von Tuple sind schreibgeschützt. Die Tupel sind also unveränderlich. In Visual Basic-Tupeln und den ValueTuple-Typen sind Tupelfelder les- und schreibbar. Die Tupel sind also änderbar.
Die generischen Tuple-Typen sind Verweistypen. Bei der Verwendung dieser Tuple-Typen werden Objekte zugeordnet. Auf dem langsamsten Pfad kann das einen messbaren Einfluss auf die Leistung Ihrer Anwendungen haben. Visual Basic-Tupel und die ValueTuple-Typen sind Werttypen.
Erweiterungsmethoden in der Klasse TupleExtensions erleichtern die Konvertierung zwischen Visual Basic-Tupeln und .NET-Objekten vom Typ Tuple. Die Methode ToTuple konvertiert ein Visual Basic-Tupel in ein .NET-Objekt vom Typ Tuple, und die Methode ToValueTuple konvertiert ein .NET-Objekt vom Typ Tuple in ein Visual Basic-Tupel.
Im folgenden Beispiel wird ein Tupel erstellt, in ein .NET-Objekt vom Typ Tuple konvertiert und wieder in ein Visual Basic-Tupel konvertiert. Dieses Tupel wird dann mit dem ursprünglichen Tupel verglichen, um zu überprüfen, ob sie gleich sind.
Dim cityInfo = (name:="New York", area:=468.5, population:=8_550_405)
Console.WriteLine($"{cityInfo}, type {cityInfo.GetType().Name}")
' Convert the Visual Basic tuple to a .NET tuple.
Dim cityInfoT = TupleExtensions.ToTuple(cityInfo)
Console.WriteLine($"{cityInfoT}, type {cityInfoT.GetType().Name}")
' Convert the .NET tuple back to a Visual Basic tuple and ensure they are the same.
Dim cityInfo2 = TupleExtensions.ToValueTuple(cityInfoT)
Console.WriteLine($"{cityInfo2}, type {cityInfo2.GetType().Name}")
Console.WriteLine($"{NameOf(cityInfo)} = {NameOf(cityInfo2)}: {cityInfo.Equals(cityInfo2)}")
' The example displays the following output:
' (New York, 468.5, 8550405), type ValueTuple`3
' (New York, 468.5, 8550405), type Tuple`3
' (New York, 468.5, 8550405), type ValueTuple`3
' cityInfo = cityInfo2 : True