初心者の頃「なんでメソッドって1つしか値返せないの?」なんて思ったことが誰しもあるんじゃないでしょうか。
私はついこの間言われました。
と、いうわけで何パターンか複数の戻り値を返す方法を紹介します。
(タプルがかなり優秀)
実際は色々な方法で複数の戻り値を返すことができます。
ここでは3つの方法と、それぞれ利点と欠点(完全に主観です。宗教的な云々はヨクワカリマセン)を見ていきます。
下に行くほど使うのが簡単で、個人的にオススメです。
サンプル
unity のコンソールにただただ値を表示するだけの内容です。これを3種類の書き方で確認します。
Windows Console / Forms の場合、適宜書き換えてください。
クラスを返す
using UnityEngine; public class Exsample1 : MonoBehaviour { class Result { public string StringValue; public int IntValue; } // Start is called before the first frame update void Start() { var result = getResult(); Debug.Log($"string:{result.StringValue}, int:{result.IntValue}"); } Result getResult() { var result = new Result(); result.StringValue = "ABCDE"; result.IntValue = 9; return result; } }
初心者に寄せて、わざと冗長な書き方をしている部分があります。
クラスにはメンバーを入れ放題なので、クラスを戻り値とするアプローチです。
メリット
形がハッキリしているので、ソースを追いかけた場合理解はしやすい。
デメリット
記述がとにかく面倒。戻り値のためにわざわざクラスを作る必要がある。
out 演算子を使う
using UnityEngine; public class Exsample2 : MonoBehaviour { // Start is called before the first frame update void Start() { string StringValue; int IntValue; getResult(out StringValue, out IntValue); Debug.Log($"string:{StringValue}, int:{IntValue}"); } void getResult(out string StringValue, out int IntValue) { StringValue = "ABCDE"; IntValue = 9; } }
戻り値やめよう! 引数の out 演算子で返せばいくつでも OK! という手段です。
メリット
クラスやインスタンスを作る必要がない分、最初の方法より記述が楽。
デメリット
大抵1つだった引数を増やす必要に迫られてから out を使いだすので、「1つは戻り値、1つは out 演算子」みたいな適当な感じの設計になりやすいです。
また、変更しなくてもいい値の場合 out ではなく ref を使う必要があり、記述時にこのへん混乱を生む可能性も。
タプル
using UnityEngine; public class Exsample3 : MonoBehaviour { // Start is called before the first frame update void Start() { var result = getResult(); Debug.Log($"string:{result.StringValue}, int:{result.IntValue}"); } (string StringValue, int IntValue) getResult() { return ("ABCDE", 9); } }
複数の引数を返したい場合、こちらが理想形になると思います。
戻り値を小カッコで囲み、型と変数名を記述するだけで、あたかもクラス定義したかのように使えます。
メリット
圧倒的に簡単。後から引数が増えた場合も、この記述であればさほど形を崩すことがない。
デメリット
歴史が浅く、存在を知らない先輩に「変なの使わないで!」とか言われるカモ。
そんな先輩はちょっと尊敬できないですね。逆に教えてあげましょう。
タプルの変数名については注意があります。
(string StringValue, int IntValue) getResult() ↓ (string, int) getResult()
このように変数名すら省略してしまった場合 Item1、Item2、Item3 ... という名前が自動的にふられます。
この状態だと使う側が、
これ、なにを返してるの?
こんな風にわからなくなってしまうので、なるべく変数名はつけた方がいいでしょう。
また、残念なのが F2: 変数名変更
(名前のリファクタ)が出来ないところ。
後で名前を変更したい場合、大人しくクラスを返すようにしておいた方がよさそうです。
バージョンによっては改善されているかもしれません。私が確認したのは .Net4.5.1 + System.ValueTuple です。
便利ではありますが、使用どころとしては「どうしても後1つだけ戻り値増やしたい…」といったシーンが適切かもしれませんね。
「コンパイラの必須型 TupleElementNamesAttribute が見つからない」というエラー
unity2020 でこのメッセージは出ませんでしたが、古い unity の場合 C#7.0 に対応した .Net になっておらず、使えない可能性があります。
Windows Forms / Console では .Net Framework 4.7 を使うか、NuGet パッケージマネージャーで System.ValueTuple を入れると使えるようになりました。(unity では未確認)