Изучение универсальности этих контейнеров

Кортежи великолепны.

Я хорошо помню дни до появления кортежей — у меня были проблемы с поиском лучшего способа вернуть несколько значений из метода. Как только кортежи были добавлены в 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, который частично решил проблему перемещения множества элементов данных, заканчивая новейшим синтаксисом для кортежей, позволяющим быстро и легко создавать структуры данных для хранения необходимых данных.

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

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

Спасибо за чтение! Следите за новостями.