ヒント
ソフトウェアの開発は初めてですか? 作業の 開始 に関するチュートリアルから始めます。 名前付き型を定義せずにメソッドまたはグループ値から複数の値を返す必要がある場合は、タプルが発生します。
別の言語で経験がありますか? C# タプルは、Python または Swift のタプルに似た値型ですが、省略可能な名前付き要素と完全な分解のサポートがあります。 C# 固有のパターンの 分解 セクションと 等値 セクションをスキミングします。
タプルは、名前付き型を定義しなくても、複数の値を単一の軽量構造体にグループ化します。 タプルは、インラインで宣言し、メソッドから戻り、個々の変数に分解できる値型です。 関連する値をすばやく一時的にグループ化する必要がある場合は、タプルを使用します。 たとえば、メソッドから複数の結果を返したり、座標ペアを格納したりする場合です。
次の例では、名前付き要素を持つタプルを作成し、各要素に名前でアクセスします。
var location = (Latitude: 47.6062, Longitude: -122.3321);
Console.WriteLine($"Location: {location.Latitude}, {location.Longitude}");
// Output: Location: 47.6062, -122.3321
タプルは、クラス、構造体、またはレコードを定義することで不要な作業が増える、短期間のグループ化に適しています。 有効期間の長いドメインの概念または動作を持つ型の場合は、 レコード、 クラス、または 構造体を優先します。 それぞれを使用するタイミングの比較については、「 種類の選択」を参照してください。
タプルを宣言して初期化する
要素の型をかっこで囲んで一覧表示して、タプルを宣言します。 必要に応じて、各要素に名前を付けて、コードをより読みやすくすることができます。
// Tuple with named elements
(string Name, int Age) person = ("Alice", 30);
Console.WriteLine($"{person.Name} is {person.Age} years old");
// Tuple with default element names (Item1, Item2)
(string, int) unnamed = ("Bob", 25);
Console.WriteLine($"{unnamed.Item1} is {unnamed.Item2} years old");
// Tuple declared with var and inline names
var city = (Name: "Seattle", Population: 749_256);
Console.WriteLine($"{city.Name}: population {city.Population}");
名前を指定しない場合、要素は既定の名前 Item1、 Item2などを使用します。 名前付き要素を使用すると、別の型定義を必要とせずにコードを自己文書化できます。
推論された要素名
コンパイラは、タプルの初期化に使用する変数名またはプロパティ名から要素名を推論します。 この機能により、名前が一致する場合の冗長性が回避されます。
var name = "Carol";
var age = 28;
// The compiler infers element names from the variable names
var person = (name, age);
Console.WriteLine($"{person.name} is {person.age}");
// Output: Carol is 28
推論された名前は、コードを簡潔に保ちます。 別の要素名が必要な場合は、明示的に指定します。
メソッドから複数の値を返す
タプルの最も一般的な用途の 1 つは、メソッドから複数の値を返す方法です。 クラスを定義したり、 out パラメーターを使用したりする代わりに、名前付き要素を持つタプルを返します。
static (double Minimum, double Maximum, double Average) ComputeStats(List<double> values)
{
var min = values.Min();
var max = values.Max();
var avg = values.Average();
return (min, max, avg);
}
名前付きタプル要素は、呼び出しサイトとメソッド シグネチャの両方で戻り値を読み取り可能にします。 呼び出し元は、位置順を覚えておく必要なく、名前で各値にアクセスできます。
タプルの分解
分解では 、タプルの要素が 1 つのステートメント内で個別の変数にアンパックされます。 タプルは、いくつかの方法で分解できます。
var point = (X: 3, Y: 7);
// Deconstruct with var (infer all types)
var (x, y) = point;
Console.WriteLine($"x={x}, y={y}");
// Deconstruct with explicit types
(int px, int py) = point;
Console.WriteLine($"px={px}, py={py}");
// Deconstruct into existing variables
int a, b;
(a, b) = point;
Console.WriteLine($"a={a}, b={b}");
// Deconstruct a method return value directly
List<double> data = [10.0, 20.0, 30.0];
var (min, max, avg) = ComputeStats(data);
Console.WriteLine($"Min: {min}, Max: {max}, Avg: {avg}");
分解は、メソッド呼び出しからタプルを受け取り、その個々の値をすぐに操作する必要がある場合に特に便利です。
foreach ループ内でタプルを直接分解できます。これにより、グループ化された値のコレクションの反復処理が簡潔になります。
List<(string Name, int Score)> results =
[
("Alice", 92),
("Bob", 87),
("Carol", 95)
];
foreach (var (name, score) in results)
{
Console.WriteLine($"{name}: {score}");
}
すべての要素が必要ない場合は、無視する各値の代わりに 破棄 (_) を使用します。 破棄された位置ごとに個別の _ を使用します。
List<double> values = [5.0, 10.0, 15.0];
var (_, max, _) = ComputeStats(values);
Console.WriteLine($"Only need the max: {max}");
// Output: Only need the max: 15
さまざまな状況でディスカードをどのように使うかの詳細については、ディスカードのページをご覧ください。
タプルの等値性
==と!=を使用してタプルを比較できます。 これらの演算子は各要素を順番に比較するため、対応するすべての要素が等しい場合、2 つのタプルが等しくなります。
var order1 = (Product: "Widget", Quantity: 5);
var order2 = (Product: "Widget", Quantity: 5);
var order3 = (Product: "Gadget", Quantity: 3);
Console.WriteLine(order1 == order2); // True
Console.WriteLine(order1 == order3); // False
// Element names don't affect equality—only values matter
var named = (X: 1, Y: 2);
var different = (A: 1, B: 2);
Console.WriteLine(named == different); // True
タプルの等価性では、各要素型で定義されている == 演算子が使用されます。つまり、 ==を定義する文字列、数値、その他の型に対して比較が正しく機能します。 要素名は等値には影響しません。値と位置のみが重要です。
非破壊的変異 with
with式では、1 つ以上の要素が変更されたタプルのコピーが作成され、元の要素は変更されません。
var original = (Name: "Widget", Price: 19.99m, InStock: true);
var discounted = original with { Price = 14.99m };
Console.WriteLine($"Original: {original.Name} at {original.Price:C}");
Console.WriteLine($"Discounted: {discounted.Name} at {discounted.Price:C}");
// Output:
// Original: Widget at $19.99
// Discounted: Widget at $14.99
このパターンは、元のタプルを変更せずに既存のタプルのバリエーションが必要な場合に便利です。
with式は、タプルに対してもレコードに対するのと同じように動作します。
辞書やルックアップにおけるタプル
タプルは、キーを複数のデータに関連付ける必要がある場合に便利なディクショナリ値を作成します。
var sizeChart = new Dictionary<string, (int Min, int Max)>
{
["Small"] = (0, 50),
["Medium"] = (51, 100),
["Large"] = (101, 200)
};
if (sizeChart.TryGetValue("Medium", out var range))
{
Console.WriteLine($"Medium: {range.Min}–{range.Max}");
}
// Output: Medium: 51–100
タプルはディクショナリ キーとしても機能し、カスタム型を定義せずに複合キーを提供します。 タプルは構造の等価性を実装するため、参照はすべての要素の結合された値と一致します。
var grid = new Dictionary<(int Row, int Column), string>
{
[(0, 0)] = "Origin",
[(1, 3)] = "Sensor A",
[(2, 5)] = "Sensor B"
};
var target = (Row: 1, Column: 3);
if (grid.TryGetValue(target, out var label))
{
Console.WriteLine($"({target.Row}, {target.Column}): {label}");
}
// Output: (1, 3): Sensor A
このパターンにより、単純な複数キーの参照やキーから複数の値へのマッピングのために、別のクラスを必要としなくなります。
タプルと匿名型
軽量の名前のないデータ構造が必要な場合は、タプルをお勧めします。 匿名型は、式ツリーのシナリオや参照型を必要とするコードで引き続き使用できますが、タプルはパフォーマンスの向上、分解のサポート、およびより柔軟な構文を提供します。 匿名型の詳細については、「 匿名型とタプル型の選択」を参照してください。
こちらも参照ください
- 完全な構文の詳細のためのタプル型 (C# リファレンス)
- ユーザー定義メソッド用のタプルとその他の型の分解
- 破棄
- 記録
.NET