メソッドから複数の値を返すいくつかの方法

TIPS
メソッドから異なる型や複数の値を返却したい場合があります。

同じ型のものを複数返す場合は配列なりコレクションなりそのインタフェースなりで返せます。

そうではなくて、たとえば、処理結果と処理成否など異なる型の値を返したい場合などはどうすればいいのでしょうか。

下のメソッドはint型の値を二つとり、加算結果を戻値として返却します。この例で考えてみます。
public int Add(int x, int y)
{
    return x + y;
}
このメソッドは引数の値が大きい場合はオーバーフローして不正な値が返却されるという不具合があります。

checkedを付与することにより、演算処理時にOverflowExceptionの有無でオーバーフロー発生有無を判断できるようになります。

この加算処理結果と処理成否との2種類の値をどう返却するかが問題となる。(こととします)。

public int Add(int x ,int y)
{
    try
    {
        return checked(x + y);
    }
    catch (OverflowException)
    {
        // どうするか?
    }
}

処理成否を例外で返却

メソッド内でOverflowExceptionを処理せずに、そのまま例外を呼び元側に伝搬させる方法。
呼び元側で都度Try-Catchを記述するのがしんどいという問題は残る。
public int Add(int x ,int y) => checked(x + y);

結果も成否も戻値で

object型の利用

演算結果がint型で処理成否がbool型だとしても、どちらもobject型から派生しています。

従って、文法上はobject型での返却が可能です。ですが大概は使い物になりません。

呼び元で返却された値の型チェックとそれによる分岐処理が必要となり煩雑に過ぎるためです。

ほとんど見かけることはない実装方法です。大概は代案があるはず。

public object Add(int x ,int y)
{
     try
     {
         return checked(x + y);
     }
     catch (OverflowException)
     {
         return false;
     }
}

特殊な値とする

例えば、必ず処理結果がプラスの値と条件付けられるような場合にマイナス値を返すとか。
若しくは失敗時にnull値とするなど。特殊値が取れるなら、現実解となりうる実装
public int Add(int x ,int y)
{
    try
    {
        return checked(x + y);
    }
    catch (OverflowException)
    {
        return -1;
    }
}
public Nullable<int> Add(int x ,int y)
{
    try
    {
        return checked(x + y);
    }
    catch (OverflowException)
    {
        return null;
    }
}

算術結果と成否とを返却する

返却用に処理結果と処理成否とをメンバとするクラスや構造体を定義し、それを返却する。
public RetValue Add(int x ,int y)
{
    try
    {
        return new RetValue { Value = checked(x + y), HasFailed = false };
    }
    catch (OverflowException)
    {
        return new RetValue { Value = 0, HasFailed = true }; ;
    }
}
 
struct RetValue
{
    public int Value { get; set; }
    public bool HasFailed { get; set; }
}
このような実装方式は返却用の型定義がどんどん増えていくのが嫌らしい。

dynamicと匿名クラスとを使う

匿名クラスにすれば余計な定義が不要。
public dynamic Add(int x ,int y)
{
    try
    {
        return new { Value = checked(x + y), HasFailed = false };
    }
    catch (OverflowException)
    {
        return new { Value = 0, HasFailed = true }; ;
    }
}

Tupleを使う

C#7.0 以降である必要があります。
Dynamic方式は実装時にインテリセンスが効かないのと、実行速度に難があるという弱点があります。
そんな時は実装時に型が決まるTupleを使えば両方の問題が解決されます。
public (int Value,bool HasFailed) Add(int x,int y)
{
    try
    {
        return (checked(x + y), false);
    }
    catch(OverflowException)
    {
        return (0, true);
    }
}

outパラメータの使用

成否を戻値で返し、演算結果はOUTパラメータで渡す方法。
戻値は1つだが、パラメータはいくつでも定義が可能。
OUTパラメータ(若しくはrefパラメータ)を使うことにより、値をいくつでも返却できる。
今回の事例なら、多分これが最適解。
public bool TryAdd(out int sum, int x ,int y)
{
    try
    {
        sum = checked(x + y);
 
        return true;
    }
    catch (OverflowException)
    {
        sum = 0;
 
        return false;
    }
}

別解

フィールドとかグローバル変数とかコンテキストとかに結果を置く。
大概はリファクタ対象であるが、たまにこういう実装が正しい場合があったりする。
public int Sum { get; private set; }
public bool HasError { get; private set; }
 
public void Add(int x, int y)
{
    try
    {
        Sum = checked(x + y);
        HasError = false;
    }
    catch (OverflowException)
    {
        Sum = 0;
        HasError = true;
    }
}
TIPS
スポンサーリンク