Обратите внимание, что все значения не обязаны относиться к тому же самому типу данных. Конструкция с круглыми скобками также применяется для присваивания кортежа переменной (или можно использовать ключевое слово
var
и тогда компилятор назначит типы данных самостоятельно). Показанные далее две строки кода делают одно и то же — присваивают предыдущий пример кортежа переменной. Переменная
values
будет кортежем с двумя свойствами
string
и одним свойством
int
.
(string, int, string) values = ("a", 5, "c");
var values = ("a", 5, "c");
По
умолчанию компилятор назначает каждому свойству имя
ItemX
, где
X
представляет позицию свойства в кортеже, начиная с 1. В предыдущем примере свойства именуются как
Item1
,
Item2
и
Item3
. Доступ к ним осуществляется следующим образом:
Console.WriteLine($"First item: {values.Item1}"); // Первый элемент
Console.WriteLine($"Second item: {values.Item2}"); // Второй элемент
Console.WriteLine($"Third item: {values.Item3}"); // Третий элемент
Кроме того, к каждому свойству кортежа справа или слева можно добавить специфическое имя. Хотя назначение имен в обеих частях оператора не приводит к ошибке на этапе компиляции, имена в правой части игнорируются, а использоваться будут имена в левой части. Показанные ниже две строки кода демонстрируют установку имен в левой и правой частях оператора, давая тот же самый результат:
(string FirstLetter, int TheNumber, string SecondLetter)
valuesWithNames = ("a", 5, "c");
var valuesWithNames2 = (FirstLetter: "a", TheNumber: 5, SecondLetter: "c");
Теперь доступ к свойствам кортежа возможен с применением имен полей, а также системы обозначений
Обратите внимание, что при назначении имен в правой части оператора должно использоваться ключевое слово
var
для объявления переменной. Установка типов данных специальным образом (даже без специфических имен) заставляет компилятор применять синтаксис в левой части оператора, назначать свойствам имена согласно системе обозначений
ItemX
и игнорировать имена, указанные в правой части. В следующих двух операторах имена
Custom1
и
Custom2
игнорируются:
(int, int) example = (Custom1:5, Custom2:7);
(int Field1, int Field2) example = (Custom1:5, Custom2:7);
Важно также понимать, что специальные имена полей существуют только на этапе компиляции и не доступны при инспектировании кортежа во время выполнения с использованием рефлексии (рефлексия раскрывается в главе 17).
Кортежи также могут быть вложенными как кортежи внутри кортежей. Поскольку с каждым свойством в кортеже связан тип данных, и кортеж является типом данных, следующий код полностью законен:
Console.WriteLine("=> Nested Tuples");
var nt = (5, 4, ("a", "b"));
Использование выведенных имен переменных (обновление в версии C# 7.1)
В C# 7.1 появилась возможность выводить имена переменных кортежей, как показано ниже:
Console.WriteLine("=> Inferred Tuple Names");
var foo = new {Prop1 = "first", Prop2 = "second"};
var bar = (foo.Prop1, foo.Prop2);
Console.WriteLine($"{bar.Prop1};{bar.Prop2}");
Понятие эквивалентности/неэквивалентности кортежей (нововведение в версии 7.3)
Дополнительным средством в версии C# 7.1 является эквивалентность (
==
) и неэквивалентность (
!=
) кортежей. При проверке на неэквивалентность операции сравнения будут выполнять неявные преобразования типов данных внутри кортежей, включая сравнение допускающих и не допускающих
null
кортежей и/или свойств. Это означает, что следующие проверки нормально работают, несмотря на разницу между