Изучение универсальности этих контейнеров
Кортежи великолепны.
Я хорошо помню дни до появления кортежей — у меня были проблемы с поиском лучшего способа вернуть несколько значений из метода. Как только кортежи были добавлены в C#, я увидел множество случаев, когда они были бы полезны.
И поскольку они разрабатывались в последующих версиях C#, они улучшались. Если вы не прикасались к ним с первых дней существования кортежей, то простите за то, что не узнали их сейчас. Они полностью изменились, но все хорошо.
Давайте рассмотрим, откуда взялись кортежи, а затем посмотрим, как мы можем их использовать сегодня.
Кортежи, появившиеся в C#4
Да, еще в 2010 году мы получили новую версию .NET и C#, и это было первое появление кортежа.
Целью этой новой концепции (то есть новой для C#) было упростить работу с небольшим количеством значений. До кортежей нам приходилось либо создавать собственный класс/структуру для хранения значений, либо использовать кучу ref
параметров — ни один из них не идеален.
Итак, у нас появился новый класс, неудивительно названный Tuple
, который мы могли использовать для хранения нескольких элементов данных в одной структуре.
Довольно просто, правда?
Мы можем определить кортеж с дженериками, чтобы указать типы данных, которые он будет содержать, а затем создать кортеж, который можно будет передавать как единую структуру.
Затем мы можем получить значения из этого кортежа:
Достаточно просто, но если вы чем-то похожи на меня, эти свойства Item1
и Item2
немного бессмысленны. Просто взятые сами по себе, вы не можете сказать, что представляет собой это значение.
Итак, первоначальная проблема решена, но есть куда стремиться.
Суперзаряженные кортежи в C# 7
Перенесемся на семь лет вперед, в 2017 год, и мы получим выпуск C# 7. Много внимания уделяется кортежам.
Нам больше не нужно использовать сам класс Tuple
, но мы можем использовать специальные конструкции в самом языке для представления того, что мы имеем в виду.
Определение нового кортежа
Во-первых, у нас есть более быстрый способ создания кортежа. Вместо создания экземпляра класса Tuple
мы можем просто заключить элементы в скобки, как показано ниже:
Довольно аккуратно, но ничего не изменилось, когда мы попытались получить данные из кортежа — он по-прежнему использует Item1
и Item2
.
Именование элементов в кортеже
Это, однако, пока вы не дадите этим элементам имена:
В сигнатуре метода мы можем добавить имена к определению кортежа. Ничего не меняется в том, как вы создаете возвращаемый кортеж, но взгляните на то, что показывает IntelliSense, когда вы пытаетесь получить данные из этой структуры:
Да, в Visual Studio 2022 (и более ранних версиях) имена этих элементов отображаются как свойства, и вы можете использовать их, как если бы они были свойствами класса. Вот код:
Теперь мы можем точно видеть, что означает каждый элемент в кортеже. И когда у вас есть кортеж с целой кучей элементов int
, вы будете благодарить меня, когда вам не нужно будет помнить, следует ли вам использовать Item5
или Item6
.
Вы также можете называть элементы при создании кортежа, что особенно полезно, если вы создаете встроенный кортеж, а не возвращаете его из метода. Вот строка кода:
Использование Var с кортежами
Как вы видите выше, мы используем var
в качестве типа локальной переменной. Это удобно, и, как я уже писал ранее, var может быть очень полезным.
Однако, если вы используете только var
, вы упускаете еще одну хитрость с кортежами. Давайте посмотрим, что предлагает Visual Studio в качестве рефакторинга этой переменной:
Здесь у нас есть два варианта:
- Используйте явный тип вместо «var»
- Деконструировать объявление переменной
Давайте рассмотрим это по очереди.
Определение кортежа как явного типа вместо var
Если мы хотим использовать явный тип вместо var
, мы используем ту же конструкцию, что и в сигнатуре метода:
Это делает действительно ясным, что включено в этот кортеж.
Однако мы можем сделать еще один шаг, который выведет кортежи на следующий уровень.
Деконструкция объявления переменной кортежа
Когда мы возвращаем кортеж из чего-то вроде метода, и нас интересует только то, что находится внутри кортежа (а не сам кортеж), мы можем разложить объявление на элементы данных и использовать их как переменные напрямую. Вот код:
Да, это может выглядеть немного странно, но возможность определять переменные прямо из кортежа, как это, действительно удобна. Остальной части вашего кода все равно, что они получены из кортежа, и вам не нужно самостоятельно определять и создавать экземпляры других локальных переменных.
Это довольно круто, правда?
Игнорирование некоторых элементов из кортежа
Но что произойдет, если вас не интересуют некоторые элементы, возвращаемые из кортежа? У вас должны быть эти дополнительные переменные, которые вы просто игнорируете?
No!
Вы можете использовать сброс для всего, что вам не нужно. Фрагмент кода прост, как вы можете видеть ниже:
Посмотрите, как вместо определения int totalItems
мы просто используем подчеркивание. Делая это, вы сообщаете компилятору, что вы не собираетесь использовать этот элемент, и поэтому он не будет загромождать им остальную часть вашего кода.
Что можно хранить в кортеже?
Внутри кортежа можно хранить практически все что угодно. Вы даже можете создать кортеж функций, если очень хотите. Вот один из способов:
Сколько элементов может поместиться в кортеж?
Если вы используете класс Tuple
старой школы, вы ограничены восемью элементами в одном кортеже. Если вам нужно больше, последним элементом может быть кортеж, и идея состоит в том, что вы вкладываете объекты кортежа друг в друга, чтобы хранить все, что вам нужно.
Однако при использовании нового синтаксиса для кортежей такого ограничения нет. Пока вы можете его написать, кортеж будет хранить его.
Интересно, что метод .ToTuple()
, доступный для кортежей, созданных таким образом, преобразует кортеж нового стиля в тип класса Tuple
. И это использует вложенные кортежи для хранения дополнительных элементов, если они слишком велики. Вот код:
Сравнение кортежей
Кортежи — это типы значений, что означает, что вы можете легко сравнивать их. Тем не менее, есть несколько вещей, о которых вам нужно знать, чтобы они вас не застали врасплох.
Если у вас есть два кортежа, содержащие одинаковые значения, проверка на равенство покажет, что они равны. Вот пример:
Если вы используете C# 7.3 и выше, вы также можете использовать ==
для этого равенства:
Первое, на что следует обратить внимание, это то, что сравнение производится по значениям и не учитывает имена элементов данных. Таким образом, если двум кортежам были даны разные имена, но они по-прежнему содержат одни и те же значения, они все равно рассматриваются как равные. Вот код:
Это важно помнить, потому что даже если именованные элементы совпадают, для сравнения используется порядок значений:
Это легче всего увидеть, если вы настроите модульный тест, чтобы проверить это:
Когда мы запускаем тест, он терпит неудачу, и мы получаем этот ответ:
Ошибка Assert.AreEqual. Ожидается:‹(1, 2, 3)›. Факт:‹(3, 2, 1)›.
Итак, вы видите, что там не перечислены имена элементов, только значения в том порядке, в котором они были установлены в кортеже.
Есть еще много вещей, о которых нужно знать при выполнении равенства кортежей — лучший ресурс для этого — Документация Microsoft по равенству кортежей.
Краткое содержание
Кортежи прошли долгий путь. Начиная с класса Tuple
, который частично решил проблему перемещения множества элементов данных, заканчивая новейшим синтаксисом для кортежей, позволяющим быстро и легко создавать структуры данных для хранения необходимых данных.
Назвать элементы внутри кортежа очень просто, чтобы вы всегда знали, к чему относятся эти данные, а синтаксис очень прост для извлечения элементов из кортежа, которые вы можете использовать позже в своем коде.
Также возможно сравнивать кортежи, но важно помнить, что для сравнения используется порядок значений в кортежах, а не именованные элементы.
Спасибо за чтение! Следите за новостями.