bucket-sort logo bucket-sort

プログラミングとインフラエンジニアリングの覚え書き

  • Posts
  • About
  • Contact
  1. Home
  2. All Posts
  3. [C#] タプル(Tuples)を深掘りする — 基本・推論変数名・等値比較・戻り値・破棄・パターンマッチング・分解

[C#] タプル(Tuples)を深掘りする — 基本・推論変数名・等値比較・戻り値・破棄・パターンマッチング・分解

Apr 16, 2026 C# , .NET bucket-sort

C# のタプル(Tuple)は、複数の値を一時的にひとまとめにする軽量なデータ構造です。クラスや構造体を定義するほどでもない場面で、複数の値をまとめて扱いたいときに便利です。C# 7.0 で構文が大きく刷新され、現在では ValueTuple ベースの簡潔な記法が主流になっています。

基本的な使い方

タプルの生成

括弧 () の中にカンマ区切りで値を並べるだけでタプルを作れます。

var point = (3, 4);
Console.WriteLine(point.Item1); // 3
Console.WriteLine(point.Item2); // 4

要素には Item1・Item2・Item3… という名前で既定アクセスできます。

要素に名前を付ける

要素に名前を付けると可読性が上がります。

var point = (X: 3, Y: 4);
Console.WriteLine(point.X); // 3
Console.WriteLine(point.Y); // 4

型として明示する場合も、同様に名前を付けられます。

(int X, int Y) point = (3, 4);
Console.WriteLine(point.X); // 3
Console.WriteLine(point.Y); // 4

複数の型を混在させる

タプルは異なる型の値を組み合わせられます。

var person = (Name: "Alice", Age: 30, IsActive: true);
Console.WriteLine(person.Name);     // Alice
Console.WriteLine(person.Age);      // 30
Console.WriteLine(person.IsActive); // True

推論された変数名(Inferred Variable Names)— C# 7.1

C# 7.1 から、変数名からタプルの要素名を自動的に推論できます。ValueTuple 内の名前を明示しなくても、元の変数名がそのまま要素名になります。

int x = 10;
int y = 20;

// C# 7.1:変数名 x・y がそのまま要素名になる
var point = (x, y);

Console.WriteLine(point.x); // 10
Console.WriteLine(point.y); // 20

オブジェクトのプロパティからも推論されます。

var user = new { Name = "Bob", Age = 25 };

var tuple = (user.Name, user.Age);

Console.WriteLine(tuple.Name); // Bob
Console.WriteLine(tuple.Age);  // 25

推論された名前はコンパイル時の名前であり、リフレクションや ToString() には現れません。

タプルの等値比較(Tuple Equality / Inequality)— C# 7.3

C# 7.3 からタプル同士を == と != で比較できます。要素数が同じで、対応する各要素を順に比較して全員一致すれば等しいと判定されます。

var a = (1, "hello");
var b = (1, "hello");
var c = (2, "hello");

Console.WriteLine(a == b); // True
Console.WriteLine(a == c); // False
Console.WriteLine(a != c); // True

要素名は比較に影響しません。構造と値が同じであれば等しいとみなされます。

var p = (X: 1, Y: 2);
var q = (A: 1, B: 2); // 名前が異なる

Console.WriteLine(p == q); // True(名前は無視される)

null を含む Nullable タプルにも対応しています。

(int, string)? x = (1, "a");
(int, string)? y = null;

Console.WriteLine(x == y); // False

メソッドの戻り値としてのタプル(Method Return Values)

タプルの最も実践的な用途の一つが複数値の返却です。out 引数やラッパークラスを作る手間が省けます。

static (int Min, int Max) GetRange(int[] values)
{
    return (values.Min(), values.Max());
}

int[] numbers = { 3, 1, 4, 1, 5, 9, 2, 6 };
var range = GetRange(numbers);

Console.WriteLine(range.Min); // 1
Console.WriteLine(range.Max); // 9

分解して受け取る

戻り値はそのまま分解して個別の変数に受け取れます(後述)。

var (min, max) = GetRange(numbers);
Console.WriteLine($"最小: {min}, 最大: {max}");

複雑な情報をまとめて返す

static (bool Success, string Message, int StatusCode) Validate(string input)
{
    if (string.IsNullOrEmpty(input))
        return (false, "入力が空です", 400);

    if (input.Length > 100)
        return (false, "入力が長すぎます", 400);

    return (true, "OK", 200);
}

var result = Validate("hello");
if (result.Success)
    Console.WriteLine($"[{result.StatusCode}] {result.Message}");

破棄(Discards)と タプル — C# 7.0

タプルの要素のうち、不要な値を捨てるには _(アンダースコア=破棄変数)を使います。

static (int Code, string Message, DateTime Timestamp) GetResult()
    => (200, "OK", DateTime.UtcNow);

// Timestamp は不要なので _ で捨てる
var (code, message, _) = GetResult();
Console.WriteLine($"{code}: {message}");

複数の不要な要素を破棄できます。

// Code だけ必要
var (_, _, timestamp) = GetResult();
Console.WriteLine(timestamp);

破棄は変数ではないため、_ を後から読み取ることはできません。意図を明確にするための構文です。

パターンマッチング switch 式とタプル(Tuple Pattern Matching switch Expressions)— C# 8.0

C# 8.0 の switch 式でタプルを使うと、複数の値の組み合わせに応じた分岐を簡潔に書けます。

状態機械の例

static string Transition(string state, string action) =>
    (state, action) switch
    {
        ("Idle",    "Start")  => "Running",
        ("Running", "Pause")  => "Paused",
        ("Paused",  "Resume") => "Running",
        ("Running", "Stop")   => "Idle",
        ("Paused",  "Stop")   => "Idle",
        _                     => throw new InvalidOperationException($"無効な遷移: {state} + {action}")
    };

Console.WriteLine(Transition("Idle",    "Start"));  // Running
Console.WriteLine(Transition("Running", "Pause"));  // Paused
Console.WriteLine(Transition("Paused",  "Resume")); // Running

FizzBuzz の例

static string FizzBuzz(int n) =>
    (n % 3 == 0, n % 5 == 0) switch
    {
        (true,  true)  => "FizzBuzz",
        (true,  false) => "Fizz",
        (false, true)  => "Buzz",
        (false, false) => n.ToString()
    };

for (int i = 1; i <= 15; i++)
    Console.WriteLine(FizzBuzz(i));

タプルと switch 式の組み合わせにより、複雑な条件分岐をテーブルのように宣言的に書けます。

タプルの分解(Deconstructing Tuples)— C# 7.0

タプルを分解すると、各要素を個別の変数に展開できます。

新しい変数への分解

var person = (Name: "Alice", Age: 30);

var (name, age) = person;
Console.WriteLine(name); // Alice
Console.WriteLine(age);  // 30

型を明示することもできます。

(string name, int age) = person;

既存の変数への分解

すでに宣言された変数にも分解できます。

string name;
int age;

(name, age) = ("Bob", 25);
Console.WriteLine($"{name}, {age}");

メソッド戻り値の即時分解

var (min, max) = GetRange(new[] { 5, 2, 8, 1, 9 });
Console.WriteLine($"min={min}, max={max}");

_ を使った部分分解

不要な要素は _ で破棄できます(前述の破棄と同じです)。

var (_, _, code) = (false, "Error", 404);
Console.WriteLine(code); // 404

クラス・構造体への Deconstruct メソッドの追加

タプルだけでなく、独自クラスにも Deconstruct メソッドを定義すると分解できます。

class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y) { X = x; Y = y; }

    public void Deconstruct(out int x, out int y)
    {
        x = X;
        y = Y;
    }
}

var point = new Point(3, 4);
var (x, y) = point; // Deconstruct が呼ばれる
Console.WriteLine($"x={x}, y={y}");

拡張メソッドとして外部から追加することもできます。

static class PointExtensions
{
    public static void Deconstruct(this Point p, out int x, out int y)
        => (x, y) = (p.X, p.Y);
}

位置パターンマッチング(Deconstructing Tuples with Positional Pattern Matching)— C# 8.0

C# 8.0 では、位置パターン(Positional Pattern) を使って switch 式・is 演算子の中でタプルや Deconstruct 持ちのオブジェクトを同時に分解しながらパターンマッチできます。

タプルの位置パターン

static string Classify(int x, int y) =>
    (x, y) switch
    {
        (0, 0)       => "原点",
        (> 0, 0)     => "X 軸正",
        (< 0, 0)     => "X 軸負",
        (0, > 0)     => "Y 軸正",
        (0, < 0)     => "Y 軸負",
        (> 0, > 0)   => "第一象限",
        (< 0, > 0)   => "第二象限",
        (< 0, < 0)   => "第三象限",
        (> 0, < 0)   => "第四象限",
        _            => "不明"
    };

Console.WriteLine(Classify(3,  4));  // 第一象限
Console.WriteLine(Classify(0,  0));  // 原点
Console.WriteLine(Classify(-1, 0));  // X 軸負

Deconstruct を持つクラスへの位置パターン

Deconstruct メソッドを定義したクラスも同様に扱えます。

class Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x, int y) { X = x; Y = y; }
    public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}

static string Describe(Point p) =>
    p switch
    {
        (0, 0)     => "原点",
        (> 0, > 0) => "第一象限",
        (< 0, > 0) => "第二象限",
        _          => "その他"
    };

Console.WriteLine(Describe(new Point(5, 3)));  // 第一象限
Console.WriteLine(Describe(new Point(0, 0)));  // 原点

is 演算子との組み合わせ

is 演算子でも位置パターンが使えます。

Point p = new Point(3, 4);

if (p is (> 0, > 0))
    Console.WriteLine("第一象限にあります");

変数への束縛も同時にできます。

if (p is (int px, int py) && px == py)
    Console.WriteLine($"x と y が等しい: {px}");

まとめ

機能 導入 要点
基本的なタプル C# 7.0 (値, 値) で複数値をまとめる
名前付き要素 C# 7.0 (Name: "Alice", Age: 30) で可読性向上
推論された変数名 C# 7.1 変数名からタプル要素名を自動推論
== / != 比較 C# 7.3 要素を順に比較して等値判定(名前は無視)
メソッド戻り値 C# 7.0 out 引数やラッパークラス不要で複数値を返せる
破棄(_) C# 7.0 不要な要素を _ で捨てる
Deconstruct C# 7.0 タプルや独自型を個別変数に展開
タプル switch 式 C# 8.0 複数値の組み合わせを宣言的に分岐
位置パターン C# 8.0 switch・is で分解しながらパターンマッチ

タプルはクラスを定義するコストなしに複数の値をまとめられる強力な機能です。一方で、長期間使い回す複雑なデータ構造には名前付きクラスや record を選ぶなど、用途に応じて使い分けることが重要です。

C# .NET タプル ValueTuple 分解 パターンマッチング
← [C#] Nullable 型を深掘りする — int?・Nullable<T>・クラスフィールド・Nullable 参照型・?? / ??= / ?. [C#] record 型を理解する — 値の等価性・with 式・class との違い →

Related Posts

  • [C#] is と as キーワードを整理する — 型チェック・安全なキャスト・パターンマッチングの使い方 Apr 30, 2026
  • [C#] switch 文のパターンマッチング — 7.0 から 9.0 までの変遷 Apr 5, 2026
  • [C#] if 文のパターンマッチング — 7.0 から 9.0 までの変遷 Apr 4, 2026
  • [C#] Finalizable & Disposable パターン実践 — Dispose パターンの完全形 May 13, 2026

Table of Contents

  • 基本的な使い方
    • タプルの生成
    • 要素に名前を付ける
    • 複数の型を混在させる
  • 推論された変数名(Inferred Variable Names)— C# 7.1
  • タプルの等値比較(Tuple Equality / Inequality)— C# 7.3
  • メソッドの戻り値としてのタプル(Method Return Values)
    • 分解して受け取る
    • 複雑な情報をまとめて返す
  • 破棄(Discards)と タプル — C# 7.0
  • パターンマッチング switch 式とタプル(Tuple Pattern Matching switch Expressions)— C# 8.0
    • 状態機械の例
    • FizzBuzz の例
  • タプルの分解(Deconstructing Tuples)— C# 7.0
    • 新しい変数への分解
    • 既存の変数への分解
    • メソッド戻り値の即時分解
    • _ を使った部分分解
    • クラス・構造体への Deconstruct メソッドの追加
  • 位置パターンマッチング(Deconstructing Tuples with Positional Pattern Matching)— C# 8.0
    • タプルの位置パターン
    • Deconstruct を持つクラスへの位置パターン
    • is 演算子との組み合わせ
  • まとめ

Recent Posts

  • [C#] Finalizable & Disposable パターン実践 — Dispose パターンの完全形 May 13, 2026
  • [C#] Disposable Objects — IDisposable / Dispose() と using 構文 May 12, 2026
  • [C#] Finalizable Objects — Finalize() の役割と使いどころ May 11, 2026
  • [C#] System.GC クラスを整理する — ガベージコレクションを制御するための API May 10, 2026
  • [C#] IComparable と IComparer — オブジェクトの順序比較と複数ソート戦略 May 9, 2026

Categories

  • C#63
  • .NET62
  • AWS27
  • Laravel16
  • Linux15
  • MySQL9
  • Apache8
  • PHP8
  • DynamoDB6
  • セキュリティ6
  • Nginx5
  • WordPress4
  • インフラ4
  • Hugo3
  • .NET Framework1
  • Aurora1
  • Filament1
  • Git1
  • SQS1

Tags

  • C#
  • .NET
  • AWS
  • Laravel
  • PHP
  • セキュリティ
  • MySQL
  • Linux
  • Apache
  • Code Snippet
  • DynamoDB
  • NoSQL
  • PHP-FPM
  • RDS
  • パフォーマンス
  • DoS
  • Nginx
  • Windows
  • WordPress
  • メモリ管理
  • 監視
  • 設計
  • Amazon Linux 2023
  • Docker
  • IDisposable
  • Ipset
  • Iptables
  • OPCache
  • Webサーバー
  • オブジェクト指向
  • クラス設計
  • コレクション
  • デザインパターン
  • パターンマッチング
  • 継承
  • 認可
  • Aurora
  • Blade
  • Grafana
  • Hugo
  • InfluxDB
  • Policy
  • Record
  • SSG
  • インターフェース
  • エラーハンドリング
  • カプセル化
  • ガベージコレクション
  • モニタリング
  • 例外
Powered by Hugo & Explore Theme.