Krotki (Visual Basic)

Począwszy od języka Visual Basic 2017, język Visual Basic oferuje wbudowaną obsługę krotki, dzięki czemu tworzenie krotki i uzyskiwanie dostępu do elementów krotki jest łatwiejsze. Krotka to uproszczona struktura danych, która ma określoną liczbę i sekwencję wartości. Podczas tworzenia wystąpienia krotki należy zdefiniować liczbę i typ danych każdej wartości (lub elementu). Na przykład krotka 2-krotka (lub para) ma dwa elementy. Pierwsza może być wartością Boolean , a druga to String. Ponieważ krotki ułatwiają przechowywanie wielu wartości w jednym obiekcie, są one często używane jako uproszczony sposób zwracania wielu wartości z metody.

Ważne

Obsługa ValueTuple krotki wymaga typu . Jeśli program .NET Framework 4.7 nie jest zainstalowany, należy dodać pakiet System.ValueTupleNuGet, który jest dostępny w galerii NuGet. Bez tego pakietu może wystąpić błąd kompilacji podobny do "Wstępnie zdefiniowany typ "ValueTuple(Of,,,)" nie jest zdefiniowany lub zaimportowany.

Tworzenie wystąpienia i używanie krotki

Tworzysz wystąpienie krotki, ujętą w nawiasy wartości rozdzielane przecinkami. Każda z tych wartości staje się polem krotki. Na przykład poniższy kod definiuje potrójną (lub 3 krotkę) z wartością Date jako pierwszą wartością, String drugą Boolean i trzecią.

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

Domyślnie nazwa każdego pola w krotku składa się z ciągu Item wraz z jednokierunkową pozycją pola w krotku. W przypadku tej 3-krotki Date pole to , String pole to Item1Item2, a Boolean pole to Item3. Poniższy przykład przedstawia wartości pól krotki utworzonej w poprzednim wierszu kodu

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

Pola krotki języka Visual Basic to odczyt i zapis; po utworzeniu wystąpienia krotki można zmodyfikować jej wartości. Poniższy przykład modyfikuje dwa z trzech pól krotki utworzonej w poprzednim przykładzie i wyświetla wynik.

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

Tworzenie wystąpienia i używanie nazwanej krotki

Zamiast używać nazw domyślnych dla pól krotki, można utworzyć wystąpienie nazwanej krotki , przypisując własne nazwy do elementów krotki. Następnie dostęp do pól krotki można uzyskać za pomocą przypisanych im nazw lub ich domyślnych nazw. Poniższy przykład tworzy wystąpienie tej samej krotki 3-krotki co poprzednio, z tą różnicą, że jawnie nazywa pierwsze pole EventDate, drugie Namei trzecie IsHoliday. Następnie wyświetla wartości pól, modyfikuje je i ponownie wyświetla wartości pól.

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

Nazwy krotek można również określić jako część deklaracji typu zmiennej, pola lub parametru:

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

lub w zwracanym typie metody.

Jest to szczególnie przydatne podczas dostarczania krotki do inicjatora kolekcji; nazwy krotek można podać w ramach deklaracji typu kolekcji:

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

Wywnioskowane nazwy elementów krotki

Począwszy od języka Visual Basic 15.3, Visual Basic może wywnioskować nazwy elementów krotki; nie trzeba ich jawnie przypisywać. Wywnioskowane nazwy krotek są przydatne podczas inicjowania krotki z zestawu zmiennych, a nazwa elementu krotki ma być taka sama jak nazwa zmiennej.

Poniższy przykład tworzy krotkę zawierającą stateInfo trzy jawnie nazwane elementy, state, stateNamei capital. Należy pamiętać, że w nazewnicjowaniu elementów instrukcja inicjowania krotki po prostu przypisuje nazwane elementy wartości identycznych nazwanych zmiennych.

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

Ponieważ elementy i zmienne mają taką samą nazwę, kompilator języka Visual Basic może wywnioskować nazwy pól, jak pokazano w poniższym przykładzie.

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

Aby włączyć nazwy elementów krotki wnioskowanej, należy zdefiniować wersję kompilatora języka Visual Basic do użycia w pliku programu Visual Basic (*.vbproj):

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

Numer wersji może być dowolną wersją kompilatora Języka Visual Basic, począwszy od wersji 15.3. Zamiast programować konkretną wersję kompilatora, możesz również określić wartość "Latest" jako wartość LangVersion kompilowania z najnowszą wersją kompilatora języka Visual Basic zainstalowanego w systemie.

Aby uzyskać więcej informacji, zobacz ustawianie wersji języka Visual Basic.

W niektórych przypadkach kompilator języka Visual Basic nie może wywnioskować nazwy elementu krotki z nazwy kandydata, a do pola krotki można odwoływać się tylko przy użyciu jego nazwy domyślnej, takiej jak Item1, Item2itp. Należą do nich:

  • Nazwa kandydata jest taka sama jak nazwa elementu członkowskiego krotki, na przykład Item3, Restlub ToString.

  • Nazwa kandydata jest duplikowana w krotki.

Gdy wnioskowanie nazwy pola kończy się niepowodzeniem, visual Basic nie generuje błędu kompilatora ani nie zgłasza wyjątku w czasie wykonywania. Zamiast tego pola krotek muszą być przywoływane przez wstępnie zdefiniowane nazwy, takie jak Item1 i Item2.

Krotki a struktury

Krotka języka Visual Basic to typ wartości, który jest wystąpieniem jednego z typów ogólnych System.ValueTuple . Na przykład holiday krotka zdefiniowana w poprzednim przykładzie jest wystąpieniem ValueTuple<T1,T2,T3> struktury. Został zaprojektowany tak, aby był lekkim kontenerem danych. Ponieważ krotka ma na celu ułatwienie tworzenia obiektu z wieloma elementami danych, brakuje mu niektórych funkcji, które mogą mieć struktura niestandardowa. Są to:

  • Niestandardowe elementy członkowskie. Nie można zdefiniować własnych właściwości, metod ani zdarzeń dla krotki.

  • Sprawdzania poprawności. Nie można zweryfikować danych przypisanych do pól.

  • Niezmienność. Krotki języka Visual Basic są modyfikowalne. Natomiast struktura niestandardowa umożliwia kontrolowanie, czy wystąpienie jest modyfikowalne, czy niezmienne.

Jeśli niestandardowe elementy członkowskie, właściwość i walidacja pola lub niezmienność są ważne, należy użyć instrukcji Visual Basic Structure do zdefiniowania niestandardowego typu wartości.

Krotka języka Visual Basic dziedziczy elementy członkowskie typu ValueTuple . Oprócz pól te obejmują następujące metody:

Metoda opis
Compareto Porównuje bieżącą krotkę z inną krotką o tej samej liczbie elementów.
Równa się Określa, czy bieżąca krotka jest równa innej krotki, czy obiektu.
GetHashCode Oblicza kod skrótu dla bieżącego wystąpienia.
ToString Zwraca reprezentację ciągu tej krotki, która przyjmuje formę (Item1, Item2...), gdzie Item1 i Item2 reprezentuje wartości pól krotki.

Ponadto typy ValueTuple implementują IStructuralComparable interfejsy i IStructuralEquatable umożliwiają definiowanie niestandardowych porównań.

Przypisania i krotki

Język Visual Basic obsługuje przypisywanie między typami krotki, które mają taką samą liczbę pól. Typy pól można przekonwertować, jeśli jedna z następujących wartości ma wartość true:

  • Pole źródłowe i docelowe mają ten sam typ.

  • Zdefiniowano konwersję rozszerzającą (lub niejawną) typu źródłowego na typ docelowy.

  • Option Strict to On, a zdefiniowana jest zawężająca (lub jawna) konwersja typu źródłowego na typ docelowy. Ta konwersja może zgłosić wyjątek, jeśli wartość źródłowa znajduje się poza zakresem typu docelowego.

Inne konwersje nie są brane pod uwagę w przypadku przypisań. Przyjrzyjmy się rodzajom przypisań dozwolonych między typami krotki.

Rozważ te zmienne używane w następujących przykładach:

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

Pierwsze dwie zmienne unnamed i anonymous, nie mają nazw semantycznych podanych dla pól. Ich nazwy pól są domyślne Item1 i Item2. Dwie ostatnie zmienne named i differentName mają nazwy pól semantycznych. Należy pamiętać, że te dwie krotki mają różne nazwy pól.

Wszystkie cztery z tych krotki mają taką samą liczbę pól (nazywanych "arity"), a typy tych pól są identyczne. W związku z tym wszystkie te przypisania działają:

' 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

Zwróć uwagę, że nazwy krotek nie są przypisane. Wartości pól są przypisywane zgodnie z kolejnością pól w krotku.

Na koniec zwróć uwagę, że możemy przypisać krotkę namedconversion do krotki, mimo że pierwsze pole named elementu to , a pierwszym polem conversionLongobiektu Integerjest . To przypisanie powiedzie się, ponieważ konwersja Integer na element Long jest konwersją rozszerzającą.

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

Krotki z różnymi liczbami pól nie można przypisać:

' 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

Krotki jako wartości zwracane przez metodę

Metoda może zwracać tylko jedną wartość. Często jednak chcesz, aby wywołanie metody zwracało wiele wartości. Istnieje kilka sposobów obejścia tego ograniczenia:

  • Można utworzyć niestandardową klasę lub strukturę, której właściwości lub pola reprezentują wartości zwracane przez metodę. Jest to rozwiązanie wagi ciężkiej; Wymaga to zdefiniowania typu niestandardowego, którego jedynym celem jest pobranie wartości z wywołania metody.

  • Możesz zwrócić pojedynczą wartość z metody i zwrócić pozostałe wartości, przekazując je przez odwołanie do metody . Obejmuje to narzut związany z utworzeniem wystąpienia zmiennej i ryzykiem nieumyślnie zastępowania wartości zmiennej przekazywanej przez odwołanie.

  • Możesz użyć krotki, która zapewnia lekkie rozwiązanie do pobierania wielu wartości zwracanych.

Na przykład metody TryParse na platformie .NET zwracają wartość wskazującąBoolean, czy operacja analizowania zakończyła się pomyślnie. Wynik operacji analizowania jest zwracany w zmiennej przekazanej przez odwołanie do metody. Zwykle wywołanie metody analizy, na przykład Int32.TryParse wygląda następująco:

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

Możemy zwrócić krotkę z operacji analizowania, jeśli zawijamy wywołanie Int32.TryParse metody we własnej metodzie. W poniższym przykładzie NumericLibrary.ParseInteger wywołuje metodę Int32.TryParse i zwraca nazwaną krotkę z dwoma elementami.

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

Następnie można wywołać metodę za pomocą kodu, takiego jak:

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

Krotki i krotki języka Visual Basic w programie .NET Framework

Krotka języka Visual Basic jest wystąpieniem jednego z typów ogólnych System.ValueTuple , które zostały wprowadzone w programie .NET Framework 4.7. Program .NET Framework zawiera również zestaw ogólnych klas System.Tuple . Te klasy różnią się jednak od krotki języka Visual Basic i typów ogólnych System.ValueTuple na wiele sposobów:

  • Elementy klas krotki są właściwościami o nazwie Item1, Item2itd. W krotkach języka Visual Basic i typach ValueTuple elementy krotki są polami.

  • Nie można przypisać znaczących nazw do elementów wystąpienia krotki lub wystąpienia valueTuple . Visual Basic umożliwia przypisywanie nazw, które komunikują znaczenie pól.

  • Właściwości wystąpienia krotki są tylko do odczytu; krotki są niezmienne. W krotkach języka Visual Basic i typach ValueTuple pola krotki są odczytywane i zapisywane; krotki są modyfikowalne.

  • Typy ogólne krotki są typami referencyjnymi. Użycie tych typów krotki oznacza przydzielanie obiektów. W przypadku ścieżek gorących może to mieć wymierny wpływ na wydajność aplikacji. Krotki języka Visual Basic i typy ValueTuple są typami wartości.

Metody rozszerzeń w TupleExtensions klasie ułatwiają konwertowanie między krotkami języka Visual Basic i obiektami krotki .NET. Metoda ToTuple konwertuje krotkę języka Visual Basic na obiekt krotki .NET, a metoda ToValueTuple konwertuje obiekt krotki platformy .NET na krotkę języka Visual Basic.

Poniższy przykład tworzy krotkę, konwertuje ją na obiekt krotki platformy .NET i konwertuje ją z powrotem na krotkę języka Visual Basic. W tym przykładzie porównano tę krotkę z oryginalną, aby upewnić się, że są one równe.

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

Zobacz też