概要
型名がなくても、メンバー名だけでその型が何をしたいものなのか十分にわかる場合があります。 このとき、むしろ、良い型名が付かない(メンバー名と重複した名前にしかならない)こともあります。
そういう場合に、「名前のない複合型」を作りたくなります。 C#には、歴史的経緯から、匿名型(anonymous type)とタプル(tuple)という2種類の「名前のない複合型」があります。
型に良い名前が付かない場合
一般的には、型にはちゃんとした名前を考えるべきです。 「型の名前だけを見れば、その型を使って何をしたいのかがわかる」というのが理想形です。 読みやすいプログラムを書くための1手法としても、「良い名前が付く単位でデータを1まとめにする」というのが非常に有効です。
しかし、常に良い名前が思いつくかというと、現実にはそうはいきません。 良い名前が付きにくい例としては以下のようなものがあります。
- メンバー名だけ見ればその型が何をしたいのか十分にわかる
- 「あるメソッドの戻り値」としか言いようのないような型
例えば、「最小値、最大値、平均値を同時に求めるメソッド」があったとしましょう。
static X Measure(IEnumerable<int> items)
{
var count = 0;
var sum = 0;
var min = int.MaxValue;
var max = int.MinValue;
foreach (var x in items)
{
sum += x;
count++;
min = Math.Min(x, min);
max = Math.Max(x, max);
}
return new X(min, max, (double)sum / count);
}
この、戻り値の型X
は、どういう名前であるべきでしょう。
メソッドがメソッドなので、「最小値と最大値と平均値」みたいな名前、すなわち、MinMaxAverage
とかでしょうか?
Measure
(計測)した結果なので、MeasureResult
とかでしょうか?
どちらも、メンバー名やメソッド名を見ればわかります。
メンバー名やメソッド名と重複した名前です。
重複は後々プログラムを修正しにくくなるのであまりいいことではありません。
例えば、メソッド名をMeasure
からTally
(勘定、計算)に変えたくなったとします。MeasureResult
もTallyResult
に変えないといけないでしょう。
返したい値として、個数と分散、中央値も増やしたくなったとします。CountMinMaxAverabeVarianceMedian
にすべきでしょうか?
こういう場合には、「名前のない型」を認めるべきです。例えば、以下のような書き方です。
static (int min, int max, double average) Measure(IEnumerable<int> items)
{
var count = 0;
var sum = 0;
var min = int.MaxValue;
var max = int.MinValue;
foreach (var x in items)
{
sum += x;
count++;
min = Math.Min(x, min);
max = Math.Max(x, max);
}
return (min, max, (double)sum / count);
}
これで十分に、「itemsの最小値(min)、最大値(max)、平均値(average)を計って(measure)返す」という意図を書き表せています。
ちなみに、この、(int min, int max, double average)
という書き方をタプルと呼びます。
タプル
(int x, int y)
というような、()
の中に型とメンバ名を並べた書き方をタプル(tuple)と呼びます。
前節の戻り値は「最小値と最大値と平均値を並べたもの」なわけですが、こういう「データを複数並べたもの」を意味する単語がタプルです。
英語では倍数を「double, triple, quadruple, ...」などという単語で表しますが、これを一般化して n-tuple (nは0以上の任意の整数)と書くことがあり、これがタプルの語源です。 n倍、n重、n連結というような意味しかなく、まさに「名前のない複合型」にピッタリの単語です。
執筆予定
(書きかけ)
タプルの書き方いろいろ
(int x, int y) p = (10, 20);
var p = (x: 10, y: 20);
var p = (x: 10, 20); var y = p.Item2;
構築(construction)、型明示(denotation), 分解(deconstruction)
主な用途は多値戻り値。引数との対比・対称性。
内部実装(ValueTuple+属性に展開)までこのページ内で触れる?別ページにする?
匿名型
anonymous type
タプルと同じページにする?分ける?
こっちはC# 3.0からある。
主な用途は射影(LINQのselectで一部のメンバーを抜き出したり、group byで複合キーに使ったり)
↑ これもやっぱり、「ある型の一部分」としか言いようがなくて、良い名前が付きにくい
値型(タプル) VS 参照型(匿名型)
タプルがValueTupleへの展開になるのに対して、匿名型はクラスの生成してる。 アセンブリをまたげない問題。リフレクション的には匿名型の方が楽。