メソッドの戻り値を「簡単に」複数にしたい(タプル)

初心者の頃「なんでメソッドって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 では未確認)

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA