CodeGym /コース /C# SELF /要素の命名とタプルの分解(deconstruction)

要素の命名とタプルの分解(deconstruction)

C# SELF
レベル 11 , レッスン 2
使用可能

1. タプルの命名:分解のためのコンテキスト

前回のレクチャーで、タプルの要素に名前を付けるやり方をやったよね。これでコードがめっちゃ読みやすくなる。無機質な Item1Item2 じゃなくて、意味のある名前(たとえば person.Name みたいな)でアクセスできる。特に、メソッドの戻り値やパラメータ、プロパティとして使うときは、要素に名前を付けるのが超大事。命名が、これからやる分解(deconstruction)を便利にするカギなんだ。

ちなみに、名前はタプルリテラルで初期化するときや、メソッド・プロパティ・フィールドの戻り値のシグネチャで付けられるよ。

メソッドの例: メソッドの戻り値で名前付きタプルを使う


public static (int 年齢, string 名前) GetPetInfo()
{
    return (年齢: 5, 名前: "バルシク");
}

// 使い方:
var info = GetPetInfo();
Console.WriteLine($"{info.名前} — {info.年齢} 歳");

フィールド/プロパティの例: フィールド/プロパティで名前付きタプル


public (int 幅, int 高さ) 画像サイズ = (1024, 768);

覚えておいて: 名前を明示的に付けなかった場合、要素は Item1Item2 みたいにアクセスできるまま。特にタプルを後で使うとき、これだとコードが読みにくくなる。だから、意味が分かりにくいときは、できるだけ要素にちゃんと名前を付けるのがおすすめ!

2. タプルの分解(deconstruction)

分解(deconstruction)って何?

分解(deconstruction)は、タプルをバラして個別の変数にすること。つまり、(年齢: 5, 名前: "バルシク") みたいなタプルから、年齢名前 という2つの変数を取り出せるってこと。

イメージ: タプルはラベル付きの箱みたいなもの。分解は、その箱の中身を一気に机の上に並べる感じ!

分解の書き方(シンタックス)


var pet = (年齢: 5, 名前: "バルシク");
var (年齢, 名前) = pet;
Console.WriteLine($"{名前} — {年齢} 歳");
タプルを変数に分解する

これで2つの変数 年齢名前 ができた。タプルから値をもらってるだけ。左側の名前(年齢, 名前)は、タプルの中の名前と一致しなくてもOK。新しいローカル変数を作ってるだけだよ。

関数の戻り値の分解

複数の値を返すためにタプルを使うことが多い。そういうとき、分解がめっちゃ便利!


public static (double 最小, double 最大) GetMinMax(int[] データ)
{
    int 最小 = データ.Min();
    int 最大 = データ.Max();
    return (最小, 最大);
}

var 数字たち = new[] { 1, 2, 3, 4, 5 };
var (最小値, 最大値) = GetMinMax(数字たち);
Console.WriteLine($"最小: {最小値}, 最大: {最大値}");
関数の戻り値の分解

分解するとき、変数名はその場に合わせて好きに付けてOK!

var で分解

var (a, b) = (10, 20); // int a = 10, b = 20

分解と discard _

タプルの全部の要素が必要なわけじゃないときもあるよね。そんなときは _(discard)で無視できる。 タプルの中の discard(_)は、「ここに要素あるのは知ってるけど、変数いらないから作らないで」って意味。

つまり、タプルを分解しつつ、いらないところは「穴」を開けてスルーできる。シンプルで便利!


var pet = (年齢: 5, 名前: "バルシク", 幸せ: true);
var (年齢, _, 幸せ) = pet; // 年齢と幸せだけ、名前は無視
discard _ を使った分解

豆知識:discardは、いらない変数で名前空間を汚さないためによく使われるよ。

foreach で分解

C#では、タプルの配列を foreach の中で直接分解できる!


var ペットたち = new (string 名前, int 年齢)[]
{
    ("バルシク", 5),
    ("ムシャ", 3),
    ("ジョニー", 7)
};

foreach (var (名前, 年齢) in ペットたち)
{
    Console.WriteLine($"{名前} — {年齢} 歳");
}
foreach でタプルを分解

3. 要素名と型付けの仕組み

名前付き要素のふるまい

タプルの要素名は「シンタックスシュガー」つまり人間のための便利機能。コンパイル時には全部 Item1Item2 みたいな内部フィールドになるけど、IDEやコンパイラが名前を覚えててくれるから、ちゃんと使える。

型の互換性とキャストへの影響

要素数と型が同じなら、名前が違ってもコンパイラ的には同じ型として扱われる。要素名は型の定義には含まれないよ:


var t1 = (X: 42, Y: 13);
var t2 = (A: 42, B: 13);
t1 = t2; // OK
Console.WriteLine(t1.X); // 42
要素名が違うタプルの互換性

ただし、式やIntelliSenseのヒントでは、左側(代入先)の名前が使われるよ。

暗黙的・明示的な名前の指定

もうやったけど、念のため。タプルは一部だけ名前を付けたり、全く付けなかったりもできる。その場合は Item1 みたいになる。


var 点 = (X: 10, 20); // XとItem2
Console.WriteLine(点.X);     // 10
Console.WriteLine(点.Item2); // 20
部分的に名前付きのタプル

アドバイス: タプルに2つ以上の要素があるときや、値の意味が分かりにくいときは、必ず名前を付けよう!

4. 名前や分解でよくあるミスや注意点

ミス1: 名前の「引き継ぎ」
先に名前付きでタプルを宣言して、あとで名前なし(または違う名前)のタプルを代入しても、IntelliSenseでは最初の名前が表示される。例:


var オリジナル = (X: 1, Y: 2);
var 別名 = オリジナル;            // 別名.X == 1, 別名.Y == 2

オリジナル = (10, 20);             // 名前なしタプル
Console.WriteLine(別名.X);      // まだXでアクセスできる(名前は残る)

ミス2: 要素名の重複
2つの要素に同じ名前は付けられない。コンパイラが「Duplicate tuple element name」って怒る:


var ダメなタプル = (A: 1, A: 2);     // エラー CS8122: Duplicate tuple element name 'A'

ミス3: 要素数が合わない分解
分解するときは、変数の数とタプルの要素数がピッタリ一致してないとダメ。違うとエラーになる:


var pet = (年齢: 5, 名前: "バルシク");
var (年齢, 名前, 気分) = pet;     // エラー CS8124: Tuple must contain exactly 3 elements

ミス4: discard _ の使い方ミス
_ で要素を無視できるけど、それぞれの場所ごとに独立してる。1つの _ で複数の要素をまとめてスキップはできない。例えば、2つの要素を1つの _ で飛ばそうとするとエラー:


var データ = (1, 2, 3);
var (_, x, _) = データ;            // OK: 1番目と3番目をスキップ
var (_, _) = データ;               // エラー CS8124: Tuple must contain exactly 2 elements

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION