Поделиться через


Выбор между анонимными типами и кортежными типами

Выбор подходящего типа включает в себя рассмотрение его удобства использования, производительности и компромиссов по сравнению с другими типами. Анонимные типы были доступны с версии C# 3.0, а универсальные System.Tuple<T1,T2> типы появились с .NET Framework 4.0. С тех пор были введены новые опции с поддержкой на уровне языка, такие как System.ValueTuple<T1,T2> — как подразумевает его название, предоставляет тип значения с гибкостью анонимных типов. В этой статье вы узнаете, когда нужно выбрать один тип над другим.

Удобство использования и функциональные возможности

Анонимные типы появились в C# 3.0 с выражениями Language-Integrated Query (LINQ). С помощью LINQ разработчики часто проектируют результаты из запросов в анонимные типы, которые содержат несколько свойств из объектов, с которыми они работают. Рассмотрим следующий пример, который создает экземпляр массива из объектов DateTime и выполняет итерацию по ним, проецируя их в анонимный тип с двумя свойствами.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var anonymous in
             dates.Select(
                 date => new { Formatted = $"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks }))
{
    Console.WriteLine($"Ticks: {anonymous.Ticks}, formatted: {anonymous.Formatted}");
}

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

Предыдущий фрагмент кода C# проектируется анонимным типом с двумя свойствами, как и следующий класс C#, созданный компилятором:

internal sealed class f__AnonymousType0
{
    public string Formatted { get; }
    public long Ticks { get; }

    public f__AnonymousType0(string formatted, long ticks)
    {
        Formatted = formatted;
        Ticks = ticks;
    }
}

Дополнительные сведения см. в анонимных типах. Та же функциональность доступна с кортежами при проецировании в запросы LINQ, и можно выбрать свойства в кортежи. Эти кортежи проходят через запрос так же, как и анонимные типы. Теперь рассмотрим следующий пример с помощью System.Tuple<string, long>.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var tuple in
            dates.Select(
                date => new Tuple<string, long>($"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {tuple.Item2}, formatted: {tuple.Item1}");
}

System.Tuple<T1,T2> Экземпляр предоставляет нумерованные свойства элементa, такие как Item1 и Item2. Эти названия свойств могут затруднить понимание намерения значений свойств, поскольку название свойства предоставляет только порядковый номер. Кроме того, System.Tuple типы являются ссылочными class типами. Однако System.ValueTuple<T1,T2> является значимым типом struct. Следующий фрагмент кода C# использует ValueTuple<string, long> для проекции в заданный контекст. Таким образом, оно назначает с использованием литерального синтаксиса.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var (formatted, ticks) in
            dates.Select(
                date => (Formatted: $"{date:MMM dd, yyyy at hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {ticks}, formatted: {formatted}");
}

Дополнительные сведения о кортежах см. в разделе Типы кортежей (справочник по C#) или Кортежи (Visual Basic).

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

Компромиссы

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

Основные отличия

Имя Модификатор доступа Тип Имя настраиваемого члена Поддержка деконструкции Поддержка дерева выражений
Анонимные типы internal class ✔️ ✔️
Tuple public class ✔️
ValueTuple public struct ✔️ ✔️

Сериализация

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

Производительность

Производительность между этими типами зависит от сценария. Основное влияние связано с компромиссом между выделением ресурсов и копированием. В большинстве случаев влияние невелико. При возникновении серьезных последствий необходимо принять меры для информирования о решении.

Заключение

Разработчику, выбирая между кортежами и анонимными типами, следует учитывать несколько факторов. Как правило, если вы не работаете с деревьями выражений, и вам удобно использовать синтаксис кортежей, тогда выберите ValueTuple, так как они предоставляют тип значения с гибкостью для именования свойств. Если вы работаете с деревьями выражений, и вы предпочитаете именовать свойства, выберите анонимные типы. В противном случае используйте Tuple.

См. также