Подтвердить что ты не робот

Когда использовать: Tuple vs Class С# 7.0

До кортежей я использовал для создания class, а его переменные затем создавал объект из этого класса и делал этот объект возвращаемым типом для некоторых функций.

Теперь с кортежами я могу сделать то же самое, и в С# 7.0 мы можем назначить понятные имена для свойств кортежей (до этого это были item1, item2 и т.д.)

Итак, теперь мне интересно, когда следует использовать кортежи и когда я должен создать класс в С# 7.0?

4b9b3361

Ответ 1

Поскольку этот ответ вызывает некоторую путаницу среди некоторых людей здесь, я должен уточнить, что - в соответствии с вопросом - все ссылки на "кортеж" здесь относятся к типу типа ValueTuple и новым синтаксическим сахарам кортежа С# 7 и в не ссылайтесь на старые ссылочные типы System.Tuple.

Итак, теперь мне интересно, когда следует использовать кортежи и когда мне следует создать класс в С# 7.0?

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

Однако есть рекомендации и правила, которыми вы можете руководствоваться при выборе между ними:

Кортежи - это значения, поэтому они копируются по значению, а не по ссылке.

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

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

Имена элементов набора не сохраняются

Имена, присвоенные элементам, используются компилятором и (в большинстве случаев) недоступны во время выполнения. Это означает, что отражение не может быть использовано для обнаружения их имен; они не могут быть доступны динамически, и они не могут использоваться в виде бритвы.

Также это важное соображение с API. Кортеж, возвращенный из метода, является исключением из правила относительно возможности обнаружения имени после компиляции. Компилятор добавляет атрибуты методу, содержащему информацию о именах кортежей. Это означает, что вы можете безопасно вернуть кортеж из общедоступного метода в одну сборку и получить доступ к его именам в другом.

Кортежи легкие

Кортежи гораздо проще писать, чем типы, поскольку они менее подробные, и объявление может быть "inlined" (т.е. объявлено в пункте использования). Это хорошо работает при объявлении метода, который возвращает несколько значений, например.

Однако, поскольку они объявлены в точке использования, если у вас есть MethodA, который вызывает MethodB, который вызывает MethodC, и каждый возвращает кортеж, вам нужно переопределить кортеж на каждом этапе. Нет (пока) способ создания псевдонима кортежа и повторного использования его по нескольким методам.

Просто используйте здравый смысл

В любой ситуации, когда вы можете использовать кортеж: просто задайте себе вопрос: "будет ли кортеж упростить код здесь". Если ответ "да", используйте его. И это в конечном счете является основным соображением о том, следует ли использовать кортеж или пользовательский класс.

Ответ 2

Вообще говоря, названные классы имеют некоторое значение в дизайне вашей системы. Они также более подробные для написания. Например, у вас может быть класс MediaFileOpener. Для дизайна важно, чтобы мы знали, что делает этот класс - мы работаем с медиа файлами!

Анонимные типы и кортежи используются, когда нет значения дизайна, и все, что вам нужно, - это легкий объект передачи данных (DTO) для перемещения информации.

Как правило, если вашему классу требуется какая-либо документация для описания того, для чего она предназначена, или если есть поведение, которое она предоставляет, используйте полный класс. Если вам нужно только временное хранилище или какая-то группировка, используйте кортеж. Рассмотрим ситуацию, когда вы хотите вернуть более одного значения из метода async. Tuple предназначен для решения этой проблемы.

Ответ 3

Кортежи предназначены для представления нескольких значений, например, когда метод намеревается возвращать несколько значений. Поддержка кортежа в С# 7 использует экземпляры System.ValueTuple<...> для представления этого набора значений. Имена этих значений действительны только в том контексте, в котором они используются и не применяются.

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

Ответ 4

Как правило, вы хотите иметь класс, когда ваш объект будет использоваться где-то в другом месте или если он представляет реальный объект или концепцию в вашем домене. Вероятно, вы собираетесь создать класс для представления автомобиля или магазина автомобилей, а не кортежей.

С другой стороны, иногда вы просто хотите вернуть пару объектов из метода. Возможно, они не представляют ничего особенного, просто нужно вернуть их вместе в этом конкретном методе. Иногда, даже если они действительно представляют собой концепцию из вашего домена (скажем, вы возвращаете (Car, Store), который может быть представлен как объект Sale), вы на самом деле не собираетесь использовать их в любом месте - вы просто перемещение данных. В этих случаях отлично использовать кортежи.

Теперь, говоря о С#, в частности, есть еще одна вещь, которую вы должны знать. Тип кортежа С# 7 на самом деле является ValueTuple, который является структурой. В отличие от классов, которые являются ссылочными типами, структуры являются типами значений. Вы можете узнать больше об этом на msdn. Самое главное, знаете, что они могут включать много копий, поэтому будьте осторожны.

Ответ 5

Использовать класс

Если ваши объекты являются объектами, которые широко используются во всем приложении, а также хранятся в каком-то постоянном хранилище, таком как реляционная база данных (SQL Server, MySQL, SQLite), база данных NoSQL или кеш (Redis, Azure DocumentDB) или даже в простых текстовых файлах или CSV.

Итак, что-то стойкое должно иметь свой собственный класс.

Использовать кортеж

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

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}

чем определить отдельный класс

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

A Tuple избавит вас от необходимости выделять память в куче, используя new для такой простой операции.

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

(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)

В этом случае нет смысла определять класс. Независимо от того, какие результаты вычислений не относятся к классу. Таким образом, альтернативой будет использование переменных out, но я считаю, что кортежи более выразительны и приводят к повышению удобочитаемости.

Ответ 6

Я хочу начать с упоминания, что у С# уже была поддержка анонимных типов. Какие ссылочные типы. Таким образом, у вас уже была подходящая альтернатива созданию именованного класса.

Одним преимуществом именованного класса является то, что будет проще использовать его (например, если вам нужен один и тот же тип в нескольких местах) и документ. Поскольку анонимный тип является анонимным, вы можете получить только переменные, на которые он был введен, если вы можете использовать var, который ограничивает контексты, в которых используются анонимные типы (например, вы не можете использовать их как типы полей, типы возврата или типы параметров).

Конечно, вы можете преодолеть некоторые ограничения анонимных типов, используя System.Tuple. Это также ссылочный тип, и вы можете использовать его явно. Недостатком является то, что ему не хватает пользовательских имен для членов.


С# 7 кортежей (ValueTuple) можно считать похожими на анонимные типы. Первое отличие состоит в том, что они являются типами значений. Это означает, что эти кортежи будут иметь преимущество в производительности, если они остаются в локальной области или перемещаются по стеку (что является общим использованием анонимных типов из-за его ограничения).

Второе отличие состоит в том, что новый синтаксис позволяет кортежа появляться в большем количестве мест, чем анонимные типы. Как вы знаете, у вас есть синтаксический сахар, чтобы определить тип возвращаемого значения ValueTuple (при использовании анонимных типов, которые вам нужно было вернуть object).

Третье отличие состоит в том, что ValueTuple поддерживает деконструкцию из коробки. Чтобы процитировать Что нового в С# 7.0:

Другой способ потребления кортежей - деконструировать их. Объявление деконструкции является синтаксисом для разбиения кортежа (или другого значения) на его части и индивидуальной привязкой этих частей к новым переменным:

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

Что вы также можете сделать с пользовательскими типами, добавив Метод деконструирования.


Для аннотации:

  • ValueTuple имеет синтаксический сахар в С# 7.0, который следует учитывать для удобочитаемости.
  • ValueTuple - тип значения. Все плюсы и минусы между использованием class и struct применяются.
  • ValueTuple может использоваться эксплицитно (с синтаксическим сахаром или без него), позволяя ему иметь универсальность System.Tuple, сохраняя именованные члены.
  • ValueTuple поддерживает деконструкцию.

Учитывая, что из этого следует, что это синтаксический сахар, я бы сказал, что более сильный аргумент для выбора ValueTuple - это тот же самый аргумент, чтобы выбрать struct. Что было бы идеально для маленьких, неизменяемых типов, которые живут в основном в стеке (так что у вас нет большого количества бокса и распаковки).

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

Я также хочу сказать, что синтаксический сахар не обязательно улучшает читаемость. Основная причина заключается в том, что вы не называете тип, а имя типа дает смысл для кода. Кроме того, вы можете добавить документацию в объявление struct или class, которое облегчит понимание.

В целом ситуация, когда ValueTuple действительно светит, возвращает несколько значений из метода. В этом случае устранение необходимости создания новых параметров out. И документация используемого ValueTuple может использоваться в документации по методу. Если вы обнаружите, что вам нужно сделать что-то еще с помощью ValueTuple (например, для определения методов расширения), я бы предложил рассмотреть возможность создания именованного типа.

Ответ 7

Я думаю, что это станет вопросом, который задают много. В настоящее время нет "лучшей практики" для того, когда использовать новые кортежи значений для класса.

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

По-моему, значение кортежа должно использоваться минимально и не более трех значений. Я думаю, что это дает хороший баланс "возвращения некоторых ценностей без необходимости в классе" и "ужасающего беспорядка ценностей". Если вам нужно вернуть более трех значений, создайте класс.

Я бы никогда не использовал кортеж, чтобы вернуться из публичного API-интерфейса, который придется использовать потребителям. Опять же, просто используйте класс.

Вот какой код реального мира, который я использовал:

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

Как только я хочу вернуть более сложные данные, я создаю класс.