いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-038 (System.Tuple)(タプル, 組オブジェクト, 4.0から追加されたクラス)

System.Tupleクラスは、.NET 4.0から追加されたクラスです。
このクラスは「組」を表します。


よく日常会話でも言う

2つで一組とか3つで一組

の感じです。


Pythonやっている人にはおなじみですね。
.NETのタプルは、ジェネリック対応してますので型がきっちり決まります。


実際には、以下のように定義されています。

Tuple<T1>
Tuple<T1, T2>
Tuple<T1, T2, T3>
Tuple<T1, T2, T3, T4>
Tuple<T1, T2, T3, T4, T5>
Tuple<T1, T2, T3, T4, T5, T6>
Tuple<T1, T2, T3, T4, T5, T6, T7>
Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>

みたまんまですが、8つ以上の組を作成する場合はTRestに含めます。
(8つ以上の組を作成する事があるのかどうかは別として)


で、オブジェクトを作成する場合は以下のようにCreate静的メソッドから作成します。

// int, intのタプル (Tuple<int, int>)
var t1 = Tuple.Create(10, 20);

// int, int, stringのタプル (Tuple<int, int, string>)
var t2 = Tuple.Create(10, 20, "hoge");


各値は、それぞれItem1, Item2, Item3....のようにして取得します。
尚、上記のプロパティは読み取り専用(readonly)です。


あらかじめToStringメソッドがオーバーライドされていますので
そのままで、以下のような文字列表現が得られます。

Console.WriteLine(Tuple.Create(10, 20));  // (10, 20)

以下、サンプルです。

    #region TupleSamples-01
    public class TupleSamples01 : IExecutable
    {
        public void Execute()
        {
            //
            // Tupleクラスは、.NET 4.0から追加されたクラスである。
            // 複数の値を一組のデータとして、保持することができる。
            // 
            // よく利用されるのは、戻り値にて複数の値を返す必要が有る場合などである。
            // (objectの配列を返すという手もあるが、その場合Boxingが発生してしまうのでパフォーマンスが
            //  厳しく要求される場面では、利用しづらい。その点、Tupleはジェネリッククラスとなっているので
            //  Boxingが発生する事がない。)
            //
            // Tupleクラスは、不可変のオブジェクトとなっている。つまり、コンストラクト時に値を設定した後は
            // その値を変更することが出来ない。(参照の先に存在しているメンバは変更可能。)
            // 各データは、「Item1」「Item2」・・・という形で取得していく。
            //
            // 以下のようなクラス定義が行われており、データの数によってインスタンス化するものが変わる。
            //     Tuple<T1>
            //     Tuple<T1, T2>
            //     Tuple<T1, T2, T3>
            //     Tuple<T1, T2, T3, T4>
            //     Tuple<T1, T2, T3, T4, T5>
            //     Tuple<T1, T2, T3, T4, T5, T6>
            //     Tuple<T1, T2, T3, T4, T5, T6, T7>
            //     Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>
            //
            // データ数が7つ以上の場合は、残りの部分をTRestとして設定する。
            //
            // Tupleを作成する際は、Tuple.Createメソッドを利用してインスタンスを取得するのが楽である。
            // また、その際は型推論を利用すると便利。
            //
            // Tupleクラスでは、ToStringメソッドがオーバーライドされており、以下のように表示される。
            //     Tuple<int, int>   ==> (xxx, yyy)
            //
            Tuple<int, string> t1 = Tuple.Create(100, "gsf_zero1");
            var                t2 = Tuple.Create(200, "gsf_zero2", 30);  // Tuple<int, string, int>となる。
            
            Console.WriteLine(t1.Item1);
            Console.WriteLine(t1.Item2);
            
            Console.WriteLine(t2.Item1);
            Console.WriteLine(t2.Item2);
            Console.WriteLine(t2.Item3);
            
            var t3 = TestMethod(10, 20);
            Console.WriteLine(t3);         // (100, 400)
            
            // 以下はエラーとなる.
            // t3.Item1 = 1000;
        }
        
        private Tuple<int, int> TestMethod(int x, int y)
        {
            return Tuple.Create(x * x, y * y);
        }
    }
    #endregion

どのような場合に使うのか?ってなるのですが
これが私自身はっきりしてません。メソッドの戻り値として複数の値を返したい場合に便利とか
引数として利用するとか聞くのですが、私の場合は、それ用のクラスなり構造体を定義する事がほとんどなので
今のところ、サンプル以外で使ったことがありません。
パパッとコード書きたい時は便利です。


MSDNには以下のような利用法があると記述されています。
以下 http://msdn.microsoft.com/ja-jp/library/dd268536.aspxy より抜粋。

・1 つのデータセットを表現するため。たとえば、データベースのレコードを組で表現し、そのレコードの各フィールドを組の構成要素で表現できます。
・データセットのアクセスと操作を容易にするため。
・out パラメーター (C# の場合) または ByRef パラメーター (Visual Basic の場合) を使用せずに、メソッドから複数の値を返すため。
・1 つのパラメーターを使用してメソッドに複数の値を渡すため。
 たとえば、Thread.Start(Object) メソッドには、起動時にスレッドが実行するメソッドに 1 つの値を渡すために使用できる 1 つのパラメーターがあります。
 このメソッドの引数として Tuple オブジェクトを指定すると、スレッドの起動ルーチンに 2 つのデータ項目を渡すことができます。

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。