C# 7 のタプル機能(そろそろC# 7について考えたのです)

C# 7 ではいくつかの機能追加が行われています。
というか、言語仕様レベルでの機能追加がまだ行われるとか、どんどん複雑化していくような危惧も持ってしまいますが・・・「デコンストラクタ」とか本当に必要なのかなぁ・・・とか思っちゃったりもするけど・・・

(技術に興味のない新卒エンジニアへの教育とか考えると、言語仕様のベースはシンプルで良い気もしちゃうので・・・)

ちなみに C# 7 っていうのは、VSでいうと「Visual Studio 2017」ってことになります。

まあ、それはさておき、今回フォーカスするのは「タプル」!

個人的には他言語(主に動的型言語勢)が先行して、.NETが後を追いかけた感のある機能ですが・・・これについてC# 7で進化するようなので、ちょっと探索してみたレポートです。

タプルとは?

タプルとは「複数のオブジェクトを1まとめとして扱うもの」です。
考え方によっては、「厳格な型定義」を軸とすると”邪道な方法論”と言われてもおかしくないけれど、柔軟な考え方をすれば「便利」というものかもしれないかなぁ、と。
あと匿名型とかDictionaryとか、似たような機能も存在するし使い分けとベストプラクティスが難しいけれど・・・

.NET 4で導入されたタプル(System.Tuple)

タプル自体は .NET 4 で「System.Tupleクラス」の導入により.NETにも取り入れられていました。
Tupleクラスの使い方は以下のような形です。

// 3つの値を持つタプルを定義
Tuple<int, string, string> emp = 
    new Tuple<int, string, string>(101, "ryuichi", "daigo");

// タプルを参照
Console.WriteLine(emp.Item1);
Console.WriteLine(emp.Item2);
Console.WriteLine(emp.Item3);

~実行出力結果~
101
ryuichi
daigo

んー、Item1、Item2、Item3と・・・まあ、そりゃそうだけど、「厳格な開発コーディング規約チーム(そのような組織がある会社であれば)」から、Tupleの使用は 承認を得にくいような仕様の機能ですよね・・・。

C# 7で導入されたタプル

で、C# 7.0におけるタプルの特徴は「名称(ラベル)に対して値を設定するタプル」を定義できることであると思います。
(これは、Swift言語なんかと同じテイストですね)
具体的な実装方法は以下の通り。

// タプルの定義
(string FirstName, string LastName, DateTime Birth) person = 
    (FirstName: "ryuichi", LastName: "daigo", Birth: new DateTime(1990, 1, 10));

// タプル値の参照
Console.WriteLine(
    string.Format("{0} {1} {2}", 
      person.FirstName, 
      person.LastName, 
      person.Birth.ToString("yyyy/MM/dd")));

(【型名】 名称, 【型名】 名称, 【型名】 名称・・・) といった形式でタプルオブジェクトを宣言し、値は (【名称】: 【値】, 【名称】: 【値】, 【名称】: 【値】, ・・・)と定義します。
利用する際は「person.FirstName」「person.LastName」「person.Birth」のように厳密な名称で参照します。仮にFirstName / LastName / Birthに該当する名称が間違っていた場合、コンパイルエラーが発生します。
また、もちろんインテリセンスも働きます。

使い方はこんな感じなのですが、私のような人間は
「これは言語仕様なのか?コンパイル後のILはどのようになっているのか?」
と気になってしまう訳で・・・
以下がコンパイル後のDLL(アセンブリ)を ILSPY で逆コンパイルしたコードになります。

// ビルド後のDLLをILSPYで逆コンパイルしたコード
ValueTuple<string, string, DateTime> person =
    new ValueTuple<string, string, DateTime>("ryuichi", "daigo", new DateTime(1990, 1, 10));
Console.WriteLine(
    string.Format("{0} {1} {2}", 
        person.Item1, 
        person.Item2, 
        person.Item3.ToString("yyyy/MM/dd")));

タプル変数定義は「ValueTuple」オブジェクトに変換されていました。
そして、各プロパティも Item1 / Item2 / Item3 と展開されています。
つまり、タプル内メンバーへの名称付きアクセスは C# 7 言語レベルでのサポート(お気遣い)であり、ILレベル / CLR レベルではタプルオブジェクトは ValueTuple型のオブジェクト、メンバーは Item 1 / Item2 / Item3・・・として扱われます。

また、ValueTupleオブジェクトについては C# 7 以前の現行のVS2015 & .NET4.6においても、Nugetで「System.ValueTuple」を参照することで利用可能です。

f:id:daigo-knowlbo:20161222010315p:plain

勿論この場合、Item1 / Item2 ・・・というようなメンバーアクセサを利用することになりますが。

まとめ

「まとめ」ってことではないですが・・・
えーと、タプルの使いどころのベストプラクティスは私自身堂々と記述できるほど確立していないというのが事実で、「シンプル」と「高機能」のバランスは難しいなあ・・・と思っております。
タプルに限らず、得られる結果への道筋・選択肢が複数ある事が最近は多く、バランスの良い規約や方向性を持つこと、技術的本質を見切ることが重要ではないかなぁ、と思っているのです。
という、モヤモヤした終わり方をするのです(笑)

では、またあ。

※2017/3/15追記
以下もご参考に
ryuichi111std.hatenablog.com