Поделиться через


Кортежи (Visual Basic)

Начиная с Visual Basic 2017 язык Visual Basic предлагает встроенную поддержку кортежей, что упрощает создание кортежей и доступ к элементам кортежей. Кортеж — это упрощенная структура данных, которая имеет определенное число и последовательность значений. При создании экземпляра кортежа определяется число и тип данных каждого значения (или элемента). Например, 2-кортеж (или пара) содержит два элемента. Первое может быть значением Boolean , а второй — Stringзначением. Так как кортежи упрощают хранение нескольких значений в одном объекте, они часто используются в качестве упрощенного способа возврата нескольких значений из метода.

Внимание

Для поддержки кортежей требуется ValueTuple тип. Если платформа .NET Framework 4.7 не установлен, необходимо добавить пакет System.ValueTupleNuGet, который доступен в коллекции NuGet. Без этого пакета может возникнуть ошибка компиляции, аналогичная описанию "Предопределенный тип ValueTuple(Of,,,)" не определен или импортирован".

Создание экземпляров и использование кортежа

Создайте экземпляр кортежа, заключив значения с разделителями-запятыми в скобки. Затем каждое из этих значений становится полем кортежа. Например, следующий код определяет тройной (или 3 кортеж) с Date его первым значением, String а второй — Boolean третьим.

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

По умолчанию имя каждого поля в кортеже состоит из строки Item вместе с одной позицией поля в кортеже. Для этого 3 кортежа Date поле имеет Item1значение , String поле равноItem2, а Boolean поле — .Item3 В следующем примере отображаются значения полей кортежа, созданных в предыдущей строке кода.

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

Поля кортежа Visual Basic доступны для чтения и записи; После создания экземпляра кортежа можно изменить его значения. Следующий пример изменяет два из трех полей кортежа, созданного в предыдущем примере, и отображает результат.

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

Создание экземпляров и использование именованного кортежа

Вместо использования имен по умолчанию для полей кортежа можно создать экземпляр именованного кортежа , назначив собственные имена элементам кортежа. Затем поля кортежа можно получить по назначенным именам или по умолчанию. В следующем примере создается экземпляр того же 3 кортежа, что и ранее, за исключением того, что он явно присваивает имя первому полю EventDate, Nameвторому и третьему IsHoliday. Затем он отображает значения полей, изменяет их и снова отображает значения полей.

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

Вы также можете указать имена кортежей в рамках объявления типа переменной, поля или параметра:

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

или в возвращаемом типе метода.

Это особенно полезно при предоставлении кортежей инициализатору коллекции; Имена кортежей можно указать в рамках объявления типа коллекции:

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

Выводимые имена элементов кортежа

Начиная с Visual Basic 15.3, Visual Basic может выводить имена элементов кортежей; Их не нужно назначать явным образом. Выводимые имена кортежей полезны при инициализации кортежа из набора переменных, а имя элемента кортежа совпадает с именем переменной.

В следующем примере создается stateInfo кортеж, содержащий три явно именованных элемента, statestateNameи capital. Обратите внимание, что при именовании элементов оператор инициализации кортежа просто назначает именованные элементы значения идентичных именованных переменных.

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

Так как элементы и переменные имеют то же имя, компилятор Visual Basic может выводить имена полей, как показано в следующем примере.

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

Чтобы включить выводимые имена элементов кортежа, необходимо определить версию компилятора Visual Basic для использования в файле проекта Visual Basic (*.vbproj):

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

Номер версии может быть любой версией компилятора Visual Basic, начиная с версии 15.3. Вместо жесткого написания определенной версии компилятора можно также указать "Последняя" в качестве значения LangVersion для компиляции с последней версией компилятора Visual Basic, установленной в вашей системе.

Дополнительные сведения см. в разделе "Настройка версии языка Visual Basic".

В некоторых случаях компилятор Visual Basic не может выводить имя элемента кортежа из имени кандидата, а поле кортежа можно ссылаться только с помощью его имени по умолчанию, например Item1, Item2и т. д. К ним относятся:

  • Имя кандидата совпадает с именем элемента кортежа, например Item3, Restили ToString.

  • Имя кандидата дублируется в кортеже.

Если вывод имени поля завершается ошибкой, Visual Basic не создает ошибку компилятора и не вызывает исключение во время выполнения. Вместо этого поля кортежа должны ссылаться на их предопределенные имена, например Item1 и Item2.

Кортежи и структуры

Кортеж Visual Basic — это тип значения, который является экземпляром одного из универсальных типов System.ValueTuple . Например, кортеж, определенный в предыдущем примере, holiday является экземпляром ValueTuple<T1,T2,T3> структуры. Он предназначен для создания упрощенного контейнера для данных. Так как кортеж предназначен для упрощения создания объекта с несколькими элементами данных, он не имеет некоторых функций, которые может иметь пользовательская структура. Например:

  • Пользовательские члены. Нельзя определить собственные свойства, методы или события кортежа.

  • Проверка. Невозможно проверить данные, назначенные полям.

  • Неизменяемость. Кортежи Visual Basic изменяются. В отличие от этого, пользовательская структура позволяет контролировать, является ли экземпляр изменяемым или неизменяемым.

Если важны пользовательские элементы, свойство и проверка полей или неизменяемость, следует использовать инструкцию Структуры Visual Basic для определения пользовательского типа значения.

Кортеж Visual Basic наследует элементы своего типа ValueTuple . Помимо полей, к ним относятся следующие методы:

Метод Description
CompareTo Сравнивает текущий кортеж с другим кортежем с тем же количеством элементов.
Равно Определяет, равен ли текущий кортеж другому кортежу или объекту.
GetHashCode Вычисляет хэш-код для текущего экземпляра .
ToString Возвращает строковое представление этого кортежа, которое принимает форму (Item1, Item2...), где Item1 и Item2 представляет значения полей кортежа.

Кроме того, типы ValueTuple реализуют IStructuralComparable и IStructuralEquatable интерфейсы, которые позволяют определять пользовательские сравнения.

Назначение и кортежи

Visual Basic поддерживает назначение между типами кортежей с одинаковым количеством полей. Типы полей можно преобразовать, если одно из следующих значений имеет значение true:

  • Исходное и целевое поле имеют одинаковый тип.

  • Определяется расширение (или неявное) преобразование исходного типа в целевой тип.

  • Option StrictOnэто , а сужающее (или явное) преобразование исходного типа в целевой тип определяется. Это преобразование может вызвать исключение, если исходное значение находится за пределами диапазона целевого типа.

Другие преобразования в контексте назначений не учитываются. Рассмотрим возможные виды назначений между типами кортежей.

В приведенных ниже примерах можно использовать указанные переменные:

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

Первые две переменные unnamed и anonymousне имеют семантических имен, предоставленных для полей. Их имена полей являются именами по умолчанию Item1 и Item2. Последние две переменные named и differentName имена семантических полей. Обратите внимание на то, что поля в этих двух кортежах называются по-разному.

Все четыре из этих кортежей имеют одинаковое количество полей (например, arity), а типы этих полей идентичны. Таким образом, все эти назначения работают:

' 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

Обратите внимание на то, что имена кортежей не назначаются. Значения полей назначаются в соответствии с порядком полей в кортеже.

Наконец, обратите внимание, что мы можем назначить named кортеж conversion кортеже, даже если первое поле является и Integerпервым полем named conversion является .Long Это назначение завершается успешно, так как преобразование Integer в объект Long является расширением.

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

Кортежи с разными числами полей не могут назначаться:

' 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

Кортежи как возвращаемые значения методов

Метод может возвращать только одно значение. Однако часто требуется вызов метода для возврата нескольких значений. Существует несколько способов обойти это ограничение:

  • Можно создать пользовательский класс или структуру, свойства или поля которых представляют значения, возвращаемые методом. Это решение в тяжелом весе; Для этого необходимо определить пользовательский тип, цель которого — получить значения из вызова метода.

  • Можно вернуть одно значение из метода и вернуть оставшиеся значения, передав их по ссылке на метод. Это включает в себя затраты на создание экземпляра переменной и рисков, которые непреднамеренно перезаписывают значение переменной, передаваемой по ссылке.

  • Кортеж можно использовать, предоставляющий упрощенное решение для получения нескольких возвращаемых значений.

Например, методы TryParse в .NET возвращают Boolean значение, указывающее, выполнена ли операция синтаксического анализа. Результат операции синтаксического анализа возвращается в переменной, передаваемой ссылкой на метод. Обычно вызов метода синтаксического анализа, например Integer.TryParse , выглядит следующим образом:

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

Мы можем вернуть кортеж из операции синтаксического анализа, если мы заключим вызов метода Integer.TryParse в нашем собственном методе. В следующем примере NumericLibrary.ParseInteger вызывает метод Integer.TryParse и возвращает именованный кортеж с двумя элементами.

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

Затем можно вызвать метод с кодом, как показано ниже:

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 в платформа .NET Framework

Кортеж Visual Basic — это экземпляр одного из универсальных типов System.ValueTuple, которые были представлены в платформа .NET Framework 4.7. Платформа .NET Framework также включает набор универсальных классов System.Tuple. Однако эти классы отличаются от кортежей Visual Basic и универсальных типов System.ValueTuple несколькими способами:

  • Элементы классов кортежей — это свойства с именем Item1, Item2и т. д. В кортежах Visual Basic и типах ValueTuple элементы кортежа являются полями.

  • Не удается назначить значимые имена элементам экземпляра кортежа или экземпляра ValueTuple . Visual Basic позволяет назначать имена, которые передают значение полей.

  • Свойства экземпляра Кортежа доступны только для чтения. Кортежи неизменяемы. В кортежах Visual Basic и типах ValueTuple поля кортежей считываются и записываются; кортежи являются изменяемыми.

  • Универсальные типы кортежей являются ссылочными типами. Использование этих типов кортежей означает выделение объектов. В критических путях это может заметно влиять на производительность приложения. Кортежи Visual Basic и типы ValueTuple — это типы значений.

Методы расширения в TupleExtensions классе упрощают преобразование между кортежами Visual Basic и объектами кортежей .NET. Метод ToTuple преобразует кортеж Visual Basic в объект Кортежа .NET, а метод ToValueTuple преобразует объект кортежа .NET в кортеж Visual Basic.

В следующем примере создается кортеж, он преобразуется в объект кортежа .NET и преобразует его обратно в кортеж Visual Basic. Затем пример сравнивает этот кортеж с исходным, чтобы убедиться, что они равны.

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

См. также