次の方法で共有


匿名型とタプル型の選択

適切な型を選択するには、他の型と比較して、使いやすさ、パフォーマンス、トレードオフを考慮する必要があります。 匿名型は C# 3.0 以降で使用できます。一方、ジェネリック System.Tuple<T1,T2> 型は .NET Framework 4.0 で導入されました。 それ以来、 System.ValueTuple<T1,T2> などの言語レベルのサポートで新しいオプションが導入されました。名前が示すように、匿名型の柔軟性を備えた値型が提供されます。 この記事では、一方の型を他の型よりも選択するのが適切な場合について説明します。

使いやすさと機能

匿名型は、Language-Integrated クエリ (LINQ) 式を使用して C# 3.0 で導入されました。 LINQ では、開発者は多くの場合、クエリの結果を匿名型に射影し、操作しているオブジェクトからいくつかの選択プロパティを保持します。 次の例では、DateTime オブジェクトの配列をインスタンス化し、それを反復処理して2つのプロパティを持つ匿名型に射影します。

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# クラスと同様に、2 つのプロパティを持つ匿名型を射影します。

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>では、インスタンスは、Item1Item2などの番号付き項目のプロパティを公開します。 これらのプロパティ名は、プロパティ名が序数のみを提供するため、プロパティ値の意図を理解するのが難しくなる可能性があります。 さらに、 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 ✔️ ✔️

シリアル化

型を選択する際の重要な考慮事項の 1 つは、シリアル化する必要があるかどうかです。 シリアル化は、オブジェクトの状態を永続化または転送できる形式に変換するプロセスです。 詳細については、 シリアル化を参照してください。 シリアル化が重要な場合は、匿名型またはタプル型よりも class または struct を作成することをお勧めします。

[パフォーマンス]

これらの型間のパフォーマンスは、シナリオによって異なります。 大きな影響は、割り当てとコピーのトレードオフです。 ほとんどのシナリオでは、影響は小さくなります。 大きな影響が生じる可能性がある場合は、決定を通知するために測定を行う必要があります。

結論

タプルと匿名型のどちらかを選択する開発者は、考慮すべきいくつかの要因があります。 一般に、 式ツリーを操作していない場合、タプル構文に慣れている場合は、プロパティに名前を付ける柔軟性を備えた値型が提供されるため、 ValueTuple を選択します。 式ツリーを使用していて、プロパティに名前を付ける場合は、匿名型を選択します。 それ以外の場合は、Tuple を使用します。

こちらも参照ください