アプリケーション部の田口(@t_tetsuzin)です。 社内では数少ないF#erとして潜伏中です。
待ちに待った VisualStudio2017 がリリースされましたね!
Graniではさっそく C# 7.0 を本番環境に投入しています。
そんな待望の C# 7.0 で使えるようになった新機能は
- タプル(ValueTuple)・タプルの分解
- Task-like
- ローカル関数
- 拡張されたswitch文(パターンマッチ)
- etc…
と大きいのから小さいのまで多岐にわたりますが、 今回はタプルの便利な使い方について紹介したいと思います。
タプル以外の今回の目玉機能である Task-like はちょうど公開された弊社CTOの記事でどうぞ!
どういう風に便利なのか
※ ValueTuple は .NET Framework 4.7 未満で使用する場合は
Nuget から System.ValueTuple をパッケージをプロジェクトに追加する必要があります。
ソーシャルゲームではユーザーの情報など大量のデータを様々なところから呼び出す必要があるので、 こういう複数の非同期処理を呼ぶ場所がよく出てきます。
public async Task<int> HogeAsync() { await HeavyFunctionAsync(); // 重い処理 return 10; } public async Task<string> FugaAsync() { await HeavyFunctionAsync(); // 重い処理 return "Fuga"; } public async Task<int[]> MogeAsync() { await HeavyFunctionAsync(); // 重い処理 return new[] { 10, 20, 30 }; }
非同期処理を並列に実行するため、今まではこのように書いていました。
public async Task Before() { // C#6までの書き方 var hogeTask = HogeAsync(); var fugaTask = FugaAsync(); var mogeTask = MogeAsync(); await Task.WhenAll(hogeTask, fugaTask, mogeTask); var hoge = hogeTask.Result; var fuga = fugaTask.Result; var moge = mogeTask.Result; // hoge, fuga, moge を使った処理へつづく await SomeFunctionAsync(hoge, fuga, moge); }
長いですね。とてもイケてるコードには見えません。
しかし、タプルと分解、そして拡張メソッドを組み合わせることで……
public async Task After() { // C#7以降でタプル記法を使った書き方 var (hoge, fuga, moge) = await (HogeAsync(), FugaAsync(), MogeAsync()).WhenAll(); // hoge, fuga, moge を使った処理へつづく await SomeFunctionAsync(hoge, fuga, moge); }
こんな風に書くことができるようになりました。
追加した拡張メソッド
public static class ValueTupleExtensions { public static async Task<(T1, T2)> WhenAll<T1, T2>(this (Task<T1>, Task<T2>) tasks) { await Task.WhenAll(tasks.Item1, tasks.Item2).ConfigureAwait(false); return (tasks.Item1.Result, tasks.Item2.Result); } public static async Task<(T1, T2, T3)> WhenAll<T1, T2, T3>(this (Task<T1>, Task<T2>, Task<T3>) tasks) { await Task.WhenAll(tasks.Item1, tasks.Item2, tasks.Item3).ConfigureAwait(false); return (tasks.Item1.Result, tasks.Item2.Result, tasks.Item3.Result); } public static async Task<(T1, T2, T3, T4)> WhenAll<T1, T2, T3, T4>(this (Task<T1>, Task<T2>, Task<T3>, Task<T4>) tasks) { await Task.WhenAll(tasks.Item1, tasks.Item2, tasks.Item3, tasks.Item4).ConfigureAwait(false); return (tasks.Item1.Result, tasks.Item2.Result, tasks.Item3.Result, tasks.Item4.Result); } }
上の例では4組のタプルまでしか挙げていませんが、8組程度なら用意してしまって良いと思います。
今回はタプルしか紹介していませんが、C# 7.0 には他にも有力な新機能が多く含まれているので、積極的に使っていきましょう! もちろん弊社のもうすぐリリースされる新タイトル黒騎士と白の魔王でも C# 7.0 が使われています!