Когда использовать: Кортеж против класса c# 7.0

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

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

Теперь мне интересно, когда я должен использовать кортежи и когда я должен создать класс В C# 7.0?

9 ответов


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

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

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

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

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

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

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

имена элементов кортежа не сохраняются

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

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

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

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

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

просто используйте common смысл

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


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

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

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


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

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

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

использовать кортежа!--7-->

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

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

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

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

Кортеж сэкономит вам время от необходимости выделять память в куче с помощью new для такой простой операции.

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

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

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


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

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


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

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

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


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

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

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


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

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

третья разница в том, что ValueTuple поддерживает деконструкцию из коробки. Цитата что нового в C# 7.0:

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

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

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


аннотация:

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

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

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

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

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


Tuple-отличный вариант, когда вы хотите объединить несколько значений (могут быть разные типы) в один объект без создания пользовательского класса. В этом случае Кортеж будет быстрым и идеальным вариантом для работы.


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

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

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

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

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

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

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


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