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

初心者の頃「なんでメソッドって値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 演算子」みたいな適当な感じの設計になりやすいです。

また、ref と out の違い(と、2つある意味)を理解していないとワケのわからない使い方をしてしまう事もあるかもしれません。

タプル

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