同じ型のものを複数返す場合は配列なりコレクションなりそのインタフェースなりで返せます。
そうではなくて、たとえば、処理結果と処理成否など異なる型の値を返したい場合などはどうすればいいのでしょうか。
下のメソッドは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;
}
}