C# 8.0 :【Value Tuple】を積極的に使っていこう
今回はC# 8.0から使えるようになったValue Tupleについて紹介します。
読み方はバリュータプルです。
ValueTupleの使い時としては、
『クラス作るほどでもないんだけど、値は複数返したい』
という時に重宝します。
実はこれに似た機能で
C#7.0から出ていたTuple(タプル)があります。
Tupleも同じく『クラスを作るほどでもないんだけど、値は複数返したい』という時に使われます。
こちらと比較しながら書いていきます。
スポンサードサーチ
使い方
例えばメソッドで値を複数返したいときがあるとします。
以下は階乗( ただし、param > 0 )を求めるメソッドで、
Value Tupleを使って返しています。
// ●ValueTuple
// 階乗を求めるメソッド
// ただしparam > 0 のみ
static (int result, int param) GetFactorial( int param ) {
var result = 1;
for ( var count = 1; count <= param; count++ ) {
result = result * count;
}
// これ↓
return (result, param);
}
こんな感じで、return (result, param) で値を返せばOKです。
では、ValueTupleに似た、従来のTuple型ではどうなるでしょうか。
// ●Tuple
static Tuple < int, int > GetFactorialTuple( int param ) {
var result = 1;
for ( var count = 1; count <= param; count++ ) {
result = result * count;
}
// tuple
return Tuple.Create(result, param);
}
一見すると、ValueTupleと似ていますね。
違いは、頭のメソッド型の記載方法と、最後のreturnするところでしょうか。
// ●ValueTuple
// ●method
static (int result, int param) GetFactorial( int param )
// ●return
return (result, param)
// ●Tuple
// ●method
static Tuple < int, int > GetFactorialTuple( int param )
// ●return
return Tuple.Create(result, param);
呼び出し元ではどうなるか
呼び出し元では、いくつか取り方があります。
こちらもValueTupleとTuple型の両方を比較しながら見ていきましょう。
ひとつで受けて、プロパティのように使う
ValueTupleを使った一番スタンダードな受け取り方は
// ●ValueTuple
// 使い方1:ひとつで受けて、プロパティのように使う
var hoge1 = GetFactorial(param);
Console.WriteLine($"hoge1: {hoge1.param}! = {hoge1.result}");
GetFactorialは先ほど紹介した階乗を求めるメソッドです。
ValueTuple型で返しています。
あたかもクラスを作ったように、
hoge1のプロパティぽくアクセスできるようになります。
では、Tuple型はどうでしょうか
// ●Tuple
// 使い方1:一つで受けて、プロパティのように使う
var hoge1 = GetFactorialTuple(param);
Console.WriteLine($"hoge1: {hoge1.Item2}! = {hoge1.Item1}");
こちらは、強制定期にItem1, Item2に置き換わってしまいます。
ValueTupleではプロパティ名のようにアクセスできたため、
どんな変数が入っているのかが分かりやすかったと思います。
しかし、Tupleでは変数にどんな値が入っているのかなどは分かりませんね。
『Item1が結果を返してくれるresultで、Item2が最初に送った引数で...』というように、
その都度、メソッドの戻り値を見て判断しなければなりません。
使い方2:二つで受ける
また、次のように分けて値を受け取ることができます。
// ●ValueTuple
// 使い方2:二つで受ける
var (result2, param2) = GetFactorial(param);
Console.WriteLine($"hoge2 : {param2}! = {result2} ");
同じく、Tupleではどうでしょうか。
// ●Tuple
// 使い方2:二つで受ける
var (result2, param2) = GetFactorialTuple(param);
Console.WriteLine($"hoge2 : {param2}! = {result2} ");
Tupleも分けて受け取れば、
Item1やItem2として受け取らなくても済みますね。
こちらは後ほど説明しましょう。
使い方3: 片方の値はとらない
次に、『メソッドとしては二つ値が返ってくるけど、片方はいらないよ!』
という場合の便利な受け取り方を紹介します。
// ●ValueTuple
// 使い方3: 片方の値はとらない
var (result3, _) = GetFactorial(param);
Console.WriteLine($"hoge3: 結果{result3}");
このように、
アンダースコアをつけることで、受け取らないこともできます。
これが利用されるケースとして、
GetFactorialメソッドがチーム全体で使われており、メソッドの中身を変更できないという時に重宝します。
受け取り側で工夫すればいいということですね。
同じくTupleでもこうなります。
// ●Tuple
//使い方3: 片方の値はとらない
var (result3, _) = GetFactorialTuple(param);
Console.WriteLine($"hoge3: 結果{result3}");
この辺りはどちらも似たような書き方になりますね。
ここらで少し疑問が出てきたと思います。
ValuTupleの『プロパティのように書くことができる』という特徴を除くと、
『あれ?それならValueTupleいらないんじゃ...?』となりますね。
従来の書き方で対応できるなら、そもそも新機能としていらないわけですから。
しかしながら、ValuTupleにはTupleと違った大きな特徴があります。
スポンサードサーチ
ValueTupleは構造体(struct)である
そうです。
一番の特徴は『ValueTupleは構造体(struct)である』ということ。
ちなみにTuple型はクラス(class)になります。
『いやいや、だから何なんだよ』
と思った方は、構造体とクラスのデータの格納場所について考えてみましょう。
クラスは参照型
構造体は値型
です。
※値型と参照型が分からない方はこちらをどうぞ
>> C#:アセった時に読む【値型と参照型】
参照型であるクラスは、
メモリ空間でいうとヒープ領域に実体があります。
そして、スタック領域には、ヒープへのアドレスだけ格納されています(これが参照という意味)。
また、値型である構造体は、スタック領域に直に格納されています。
つまり、
構造体であるValueTupleは、クラスであるTupleに比べ、
『スタック領域に直に格納されている分、アクセス速度が速い』という事です。
また、C#ではクラスのようにnew すること自体かなりのコストだったするので、
負担を軽減できるとところはなるべくValueTupleを使ってあげるとPCに優しいアプリができます。
ですが、といっても、
戻り値がやたらめったら多いときは素直にクラスに直しましょう。
私の感覚としては、
戻り値が3つになったらクラスに置き換えるようにしています。
まとめ
やったこと:C#の新機能であるValueTupleについての紹介
まとめ
- ValueTuple は構造体で負担が少ない
- プロパティのようにアクセスできる
- 戻り値が多いときは素直にクラスにする
です。