C# 8.0 :【Value Tuple】を積極的に使っていこう

C#

今回は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 は構造体で負担が少ない
  • プロパティのようにアクセスできる
  • 戻り値が多いときは素直にクラスにする

です。