Tupplar (Visual Basic)

Från och med Visual Basic 2017 erbjuder Visual Basic-språket inbyggt stöd för tupplar som gör det enklare att skapa tupplar och komma åt elementen i tupplar. En tuppeln är en enkel datastruktur som har ett specifikt tal och en sekvens med värden. När du instansierar tuppeln definierar du talet och datatypen för varje värde (eller element). Till exempel har en 2-tuppeln (eller ett par) två element. Det första kan vara ett Boolean värde, medan det andra är ett String. Eftersom tupplar gör det enkelt att lagra flera värden i ett enda objekt används de ofta som ett enkelt sätt att returnera flera värden från en metod.

Viktigt!

Tuppelns stöd kräver typen ValueTuple . Om .NET Framework 4.7 inte är installerat måste du lägga till NuGet-paketet System.ValueTuple, som är tillgängligt i NuGet-galleriet. Utan det här paketet kan du få ett kompileringsfel som liknar "Den fördefinierade typen "ValueTuple(Of,,,)" har inte definierats eller importerats."

Instansiera och använda en tuppeln

Du instansierar en tupplar genom att omsluta dess kommaavgränsade värden inom parenteser. Vart och ett av dessa värden blir sedan ett fält i tuppeln. Följande kod definierar till exempel en trippel (eller 3-tuppel) med ett Date som sitt första värde, en String som dess andra och en Boolean som dess tredje.

Dim holiday = (#07/04/2017#, "Independence Day", True)

Som standard består namnet på varje fält i en tuppeln av strängen Item tillsammans med fältets enbaserade position i tuppeln. För den här 3-tuppeln är Item1fältet Date , fältet String är Item2och fältet Boolean är Item3. I följande exempel visas värdena för fälten i tuppeln som instansieras i föregående kodrad

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

Fälten i en Visual Basic-tuppeln är skrivskyddade. När du har instansierat en tuppeln kan du ändra dess värden. I följande exempel ändras två av de tre fälten i tuppeln som skapades i föregående exempel och resultatet visas.

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

Instansiera och använda en namngiven tuppeln

I stället för att använda standardnamn för en tuppelns fält kan du instansiera en namngiven tuppeln genom att tilldela dina egna namn till tuppelns element. Tuppelns fält kan sedan nås av deras tilldelade namn eller med deras standardnamn. I följande exempel instansierar samma 3-tuppeln som tidigare, förutom att det uttryckligen namnger det första fältet EventDate, det andra Nameoch det tredje IsHoliday. Sedan visas fältvärdena, ändrar dem och visar fältvärdena igen.

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

Du kan också ange tuppelns namn som en del av typdeklarationen för en variabel, ett fält eller en parameter:

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

eller i returtypen för en metod.

Detta är särskilt användbart när du tillhandahåller tupplar till en insamlingsinitierare. tuppelns namn kan anges som en del av samlingens typdeklaration:

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

Anledda tupppelelementnamn

Från och med Visual Basic 15.3 kan Visual Basic härleda namnen på tuppelelement. du behöver inte uttryckligen tilldela dem. Härledda tupplar är användbara när du initierar en tupplar från en uppsättning variabler och vill att tuppelns elementnamn ska vara samma som variabelnamnet.

I följande exempel skapas en stateInfo tupplar som innehåller tre explicit namngivna element, state, stateNameoch capital. Observera att när du namnger elementen tilldelar tuppelns initieringsinitieringssats helt enkelt de namngivna elementen värdena för de identiskt namngivna variablerna.

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

Eftersom element och variabler har samma namn kan Visual Basic-kompilatorn härleda namnen på fälten, vilket visas i följande exempel.

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

Om du vill aktivera angivna tupppelelementnamn måste du definiera vilken version av Visual Basic-kompilatorn som ska användas i visual basic-projektfilen (*.vbproj):

<PropertyGroup>
  <LangVersion>15.3</LangVersion>
</PropertyGroup>

Versionsnumret kan vara valfri version av Visual Basic-kompilatorn från och med 15.3. I stället för att hårdkoda en specifik kompilatorversion kan du också ange "Senaste" som värdet LangVersion för att kompilera med den senaste versionen av Visual Basic-kompilatorn installerad på systemet.

Mer information finns i ställa in visual basic-språkversionen.

I vissa fall kan visual basic-kompilatorn inte härleda tuppelns elementnamn från kandidatnamnet, och tuppelns fält kan bara refereras med dess standardnamn, till exempel Item1, Item2osv. Dessa inkluderar:

  • Kandidatnamnet är samma som namnet på en tupppelmedlem, till exempel Item3, Resteller ToString.

  • Kandidatnamnet dupliceras i tuppeln.

När inferensen för fältnamn misslyckas genererar Visual Basic inte något kompilatorfel, och inte heller genereras ett undantag vid körning. I stället måste tuppelns fält refereras till av deras fördefinierade namn, till exempel Item1 och Item2.

Tupplar kontra strukturer

En Visual Basic-tuppeln är en värdetyp som är en instans av någon av de allmänna typerna System.ValueTuple . Tuppeln holiday som definierades i föregående exempel är till exempel en instans av ValueTuple<T1,T2,T3> strukturen. Den är utformad för att vara en lätt container för data. Eftersom tuppeln syftar till att göra det enkelt att skapa ett objekt med flera dataobjekt, saknar den några av de funktioner som en anpassad struktur kan ha. Dessa kan vara:

  • Anpassade medlemmar. Du kan inte definiera dina egna egenskaper, metoder eller händelser för en tuppeln.

  • Validering. Du kan inte verifiera de data som tilldelats fält.

  • Oföränderlighet. Visual Basic-tupplar kan ändras. Med en anpassad struktur kan du däremot styra om en instans är föränderlig eller oföränderlig.

Om anpassade medlemmar, egenskaper och fältvalidering eller oföränderlighet är viktiga bör du använda instruktionen Visual Basic Structure för att definiera en anpassad värdetyp.

En Visual Basic-tuppeln ärver medlemmarna av dess ValueTuple-typ . Förutom fälten innehåller dessa följande metoder:

Metod beskrivning
CompareTo Jämför den aktuella tuppeln med en annan tupplar med samma antal element.
Lika med Avgör om den aktuella tuppeln är lika med en annan tupplar eller ett annat objekt.
GetHashCode Beräknar hash-koden för den aktuella instansen.
ToString Returnerar strängrepresentationen av den här tuppeln, som har formatet (Item1, Item2...), där Item1 och Item2 representerar värdena för tuppelns fält.

Dessutom implementerar IStructuralComparable och IStructuralEquatable gränssnitt för ValueTuple-typerna, som gör att du kan definiera anpassade jämförelseobjekt.

Tilldelning och tupplar

Visual Basic stöder tilldelning mellan tuppelns typer som har samma antal fält. Fälttyperna kan konverteras om något av följande är sant:

  • Käll- och målfältet är av samma typ.

  • En utvidgad (eller implicit) konvertering av källtypen till måltypen definieras.

  • Option Strict är On, och en begränsad (eller explicit) konvertering av källtypen till måltypen definieras. Den här konverteringen kan utlösa ett undantag om källvärdet ligger utanför måltypens intervall.

Andra konverteringar beaktas inte för tilldelningar. Nu ska vi titta på vilka typer av tilldelningar som tillåts mellan tupplar.

Tänk på de här variablerna som används i följande exempel:

' 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")

De två första variablerna och unnamedanonymous, har inte semantiska namn för fälten. Deras fältnamn är standard Item1 och Item2. De två sista variablerna named och differentName har semantiska fältnamn. Observera att dessa två tupplar har olika namn för fälten.

Alla fyra av dessa tupplar har samma antal fält (kallas "arity") och typerna av dessa fält är identiska. Därför fungerar alla dessa tilldelningar:

' 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

Observera att namnen på tupplar inte har tilldelats. Fältens värden tilldelas efter ordningen på fälten i tuppeln.

Observera slutligen att vi kan tilldela named tuppeln till conversion tuppeln, även om det första fältet named i är ett Integer, och det första fältet conversion i är en Long. Den här tilldelningen lyckas eftersom konverteringen Integer till en Long är en bredare konvertering.

' 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)

Tupplar med olika antal fält kan inte tilldelas:

' 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

Tupplar som metodreturvärden

En metod kan bara returnera ett enda värde. Men ofta vill du att ett metodanrop ska returnera flera värden. Det finns flera sätt att kringgå den här begränsningen:

  • Du kan skapa en anpassad klass eller struktur vars egenskaper eller fält representerar värden som returneras av metoden. Detta är en tungviktslösning. Det kräver att du definierar en anpassad typ vars enda syfte är att hämta värden från ett metodanrop.

  • Du kan returnera ett enda värde från metoden och returnera de återstående värdena genom att skicka dem med referens till metoden. Detta innebär att instansiera en variabel och riskerar att oavsiktligt skriva över värdet för den variabel som du skickar med referens.

  • Du kan använda en tuppeln, som ger en enkel lösning för att hämta flera returvärden.

Till exempel returnerar TryParse-metoderna i .NET ett Boolean värde som anger om parsningsåtgärden lyckades. Resultatet av parsningsåtgärden returneras i en variabel som skickas med referens till metoden. Normalt ser ett anrop till en parsningsmetod ut så Int32.TryParse här:

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

Vi kan returnera en tupplar från parsningsåtgärden om vi omsluter anropet Int32.TryParse till metoden i vår egen metod. I följande exempel NumericLibrary.ParseInteger anropar metoden och returnerar en namngiven Int32.TryParse tuppl med två element.

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

Du kan sedan anropa metoden med kod som följande:

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-tupplar och tupplar i .NET Framework

En Visual Basic-tuppeln är en instans av en av de allmänna systemtyperna System.ValueTuple , som introducerades i .NET Framework 4.7. .NET Framework innehåller också en uppsättning generiska System.Tuple-klasser . De här klasserna skiljer sig dock från Visual Basic-tupplar och de allmänna typerna System.ValueTuple på flera olika sätt:

  • Elementen i tuppelns klasser är egenskaper med namnet Item1, Item2och så vidare. I Visual Basic-tupplar och ValueTuple-typerna är tupplar fält.

  • Du kan inte tilldela meningsfulla namn till elementen i en Tuppeln-instans eller en ValueTuple-instans . Med Visual Basic kan du tilldela namn som förmedlar innebörden av fälten.

  • Egenskaperna för en tupplarinstans är skrivskyddade. Tupplar är oföränderliga. I Visual Basic-tupplar och ValueTuple-typerna är tuppelns fält läs-och-skriv- och tupplar kan ändras.

  • De generiska tuppelns typer är referenstyper. Att använda dessa tupppeltyper innebär att allokera objekt. På heta sökvägar kan detta ha en mätbar inverkan på programmets prestanda. Visual Basic-tupplar och ValueTuple-typerna är värdetyper.

Tilläggsmetoder i TupleExtensions klassen gör det enkelt att konvertera mellan Visual Basic-tupplar och .NET Tuple-objekt . Metoden ToTuple konverterar en Visual Basic-tupplar till ett .NET Tuple-objekt , och metoden ToValueTuple konverterar ett .NET Tuple-objekt till en Visual Basic-tupplar.

I följande exempel skapas en tupplar, konverterar den till ett .NET Tuple-objekt och konverterar tillbaka den till en Visual Basic-tupplar. Exemplet jämför sedan den här tuppeln med den ursprungliga för att säkerställa att de är lika.

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

Se även