Dela via


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 tupel är en enkel datastruktur som har ett specifikt antal och en specifik ordning av värden. När du instansierar tuppeln definierar du talet och datatypen för varje värde (eller element). Till exempel har en 2-tuppel 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!

Stöd för tupler 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 tuppel genom att omsluta dess kommaavgränsade värden i 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 standardinställning består namnet på varje fält i en tuppel av strängen Item tillsammans med fältets enbasindexerade position i tuppeln. För den här 3-tuppeln är Datefältet Item1 , 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-tuppel är läs- och skrivbara. När du har instansierat en tuppel 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

Att instansera och använda en namngiven tupl

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 genom deras tilldelade namn eller via 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 samlingens initialiserare. Tupplernas namn kan anges som en del av samlingstypens deklaration.

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

Härledda tupelelementnamn

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 tuppelnamn är användbara när du initierar en tuppel från en samling variabler och vill att tuppelns elementnamn ska vara samma som variabelnamnet.

I följande exempel skapas en stateInfo tupel som innehåller tre explicit namngivna element, state, stateName, och capital. Observera att när du namnger elementen tilldelar tuppelns initieringssats de namngivna elementen värdena från de variabler som har samma namn.

Const state As String = "MI"
Const stateName As String = "Michigan"
Dim stateInfo = (state:=state, stateName:=stateName)
Console.WriteLine($"{stateInfo.stateName}: 2-letter code: {stateInfo.state}")
' 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:

  • Kandidatens namn är detsamma som namnet på en tuplemedlem, till exempel Item3, Rest, eller ToString.

  • Kandidatnamnet dupliceras i tuppeln.

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

Tupplar kontra strukturer

En Visual Basic-tuple är en värdetyp som är en instans av en av de generiska 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 tupl.

  • 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-tuppel ärver medlemmarna från sin ValueTuple-typ. Förutom fälten innehåller dessa följande metoder:

Metod beskrivning
CompareTo Jämför den aktuella tuppeln med ett annat tuppel med samma antal element.
Lika med Avgör om den aktuella tuplen är lika med en annan tuplar eller ett annat objekt.
GetHashCode (på engelska) 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 ValueTuple-typerna IStructuralComparable och IStructuralEquatable-gränssnitt, vilket gör att du kan definiera anpassade jämförelseobjekt.

Tilldelning och tupplar

Visual Basic stöder tilldelning mellan tuplar med 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 tupltyper.

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 dessa tupplar har samma antal fält (refereras till som "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-tupeln till conversion-tupeln, även om det första fältet i named är ett Integer och det första fältet i conversion är en Long. Den här tilldelningen lyckas eftersom konverteringen Integer till en Long är en utvidgningskonvertering.

' 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 tupl, som ger en lättviktig lösning för att hämta flera returnerade vä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 som Integer.TryParse ut så 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 tuppel från tolkningsoperationen om vi omsluter anropet metoden Integer.TryParse i vår egen metod. I följande exempel NumericLibrary.ParseInteger anropar metoden Integer.TryParse och returnerar en namngiven tuppel 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-tuple är en instans av en av de generiska typerna 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 Tuple-instans är endast läsbar; tupel är oföränderliga. I Visual Basic-tupplar och ValueTuple-typerna är fälten i tupplarna läs- och skrivbara; tupplarna är muterbara.

  • Generiska Tuple-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 skapar en tupl som konverteras till ett .NET Tuple-objekt och konverteras tillbaka till en Visual Basic-tupl. 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