みなさんこんにちは!ヒロポンです!
C# で「1つのメソッドから複数の戻り値を返したい」時、みなさんどう書いてますか??
計算した結果と、成功したかどうか。開始日と終了日をセットで。業務系の画面まわりを触ってると、こういう場面が地味に多いんですよね。
私は長いこと、この手のやつをぜんぶ out 引数で書いてました。で、ある日、自分のコードを見返した。引数が out だらけで、呼び出し側がもう何がなんだか分からん。自分で書いたのに、うわ読みにくい…ってなったやつです。
というわけで今回は、C# で複数の戻り値を返す書き方を out 引数 / ValueTuple / 専用クラス の3つに整理します。それぞれのコードと、現場でどう使い分けるかまで。コピペで動くやつを載せてるので、手元で試しながら読んでもらえると早いです。
結論: out / ValueTuple / 専用クラスはこう使い分ける
先に答えを出します。C# で複数の戻り値を返す時は、こう考えるとラクです。
- out 引数 … 返したい値が実質1個で、成功したかをおまけで返すだけなら(
int.TryParseみたいなやつ) - ValueTuple … 2〜3個の値をサッと返したい時。今はこれが一番手軽
- 専用クラス … 返す値が増えてきて、それぞれに名前や意味を持たせたい時
ValueTuple というのは、(int, string) みたいに複数の値をまとめて1つとして扱える構造体(値型)のことです。C# 7 から入った書き方で、これを覚えると out 地獄からだいぶ解放されます。
まずはそれぞれ、実際のコードで見ていきましょう。
なぜ out 引数だらけになるのか
そもそもなんで out を使うのか。C# のメソッドは、return で返せる値が基本1個だけだからです。
2個以上返したいとなると、素直に思いつくのが out 引数なんですよね。呼び出し側の変数に直接書き込んでもらう形です。
これ、返す値が増えるほど呼ぶ側がつらくなります。Method(a, b, out x, out y, out z) みたいになると、どれが入力でどれが出力なのか、パッと見で分からない。ん?これ全部 out にする必要ある?って自分でも思うやつです。
そこで登場するのが ValueTuple と専用クラスです。
書き方1: out 引数 — 1個だけ「ついで」に返すなら今も現役
まずは基本の out から。入力値をチェックして、OKかどうかとNGの理由を返す例で見ていきます。
// 入力チェック: 成功なら true、失敗なら false + 理由を out で返す
public static bool Validate(string input, out string error)
{
if (string.IsNullOrEmpty(input))
{
error = "未入力です";
return false;
}
if (input.Length > 20)
{
error = "20文字以内で入力してください";
return false;
}
error = "";
return true;
}
呼び出し側はこんな感じで書けます。
if (!Validate(userName, out string error))
{
Console.WriteLine(error); // NG の時だけ理由が入る
}
実行結果:

これ、int.TryParse とまったく同じ形なんですよね。成功したかを bool で返して、中身は out で受け取る。返す値が実質1個(+成功フラグ)の時は、out が今でも一番素直です。
逆に、返す値が2個3個と増えてくると out はしんどい。次いきましょう。
書き方2: ValueTuple — 2〜3個をサッと返す定番
C# 7 以降なら、複数の値は ValueTuple で返すのが一番手軽です。さっきの検証を書き換えるとこうなります。
// (成功したか, 理由) をまとめて返す
public static (bool ok, string error) Validate2(string input)
{
if (string.IsNullOrEmpty(input))
return (false, "未入力です");
if (input.Length > 20)
return (false, "20文字以内で入力してください");
return (true, "");
}
呼び出し側は、いい感じに分解して受け取れます。
var (ok, error) = Validate2(userName);
if (!ok)
{
Console.WriteLine(error);
}
実行結果:

コツは、(bool ok, string error) のように 名前付き にしておくこと。ok error という名前で受け取れるので、呼ぶ側が読みやすい。名前を付けない (bool, string) でも動くには動きますが、その場合は .Item1 .Item2 でアクセスすることになって、結局分かりにくくなります。
これ、どっちが読みやすいと思いますか??私は断然、名前付きの方が好きです。
書き方3: 専用クラス — 値に「意味」が増えてきたら
返す値が4個5個と増えたり、その塊を他のメソッドにも渡し回したくなったら。専用のクラスを作った方がスッキリします。
// 検証結果を表すクラス
public class ValidationResult
{
public bool Ok { get; set; }
public string Error { get; set; }
}
public static ValidationResult Validate3(string input)
{
if (string.IsNullOrEmpty(input))
return new ValidationResult { Ok = false, Error = "未入力です" };
if (input.Length > 20)
return new ValidationResult { Ok = false, Error = "20文字以内で入力してください" };
return new ValidationResult { Ok = true, Error = "" };
}
クラスにしておくと、後から「エラーコードも足したい」となった時に、プロパティを1個追加するだけで済みます。ValueTuple だと戻り値の型をあちこち直すことになるので、育っていく値の塊はクラスの方が強いです。
C# 9(.NET 5 以降)が使える環境なら、record を使うと public record ValidationResult(bool Ok, string Error); の1行で同じものが書けます。ただ Framework 4.7.2 だと record は使えないので、その場合は上の普通の class でいきましょう。
before / after: out 引数だらけを ValueTuple に直す
じゃあ実際に out だらけのメソッドを ValueTuple に書き換えると、どのくらいスッキリするのか。配列の合計・最大・最小をまとめて返す例で比べてみます。

実行結果(after・ValueTuple 版。before の out 3つ版と同じ sum/max/min を返す):

after の方、呼ぶ側が var (s, mx, mn) = Summarize(nums); の1行で済むのが気持ちいい。入力と出力がごちゃ混ぜにならないので、後から読む人にやさしいんですよね。めっちゃスッキリする!!
結局どれを使う? 3つの使い分け早見表
観点ごとに3つを並べるとこうなります。

out はTryパターンでOK/NGを返すだけに絞る。日常の複数戻り値は ValueTuple。値が育ってきたらクラス。だいたいこの3段で整理できます。
ハマりポイント3つ(知らないと地味に時間を溶かす)
使い分けの話はここまで。ここからは、私が実際に踏んだハマりどころを3つ。どれも知ってれば10分、知らないと数十分持っていかれるやつです。
① ValueTuple が見つからないと言われたら
古い環境でタプル構文を書くと、型または名前空間名 'ValueTuple' が見つかりませんでした みたいなエラーが出ることがあります。
これ、Framework のバージョンで事情が変わります。System.ValueTuple が framework 本体に入ったのは .NET Framework 4.7 から で、4.7.2 なら標準で使えます(NuGet 不要)。逆に 4.6.2 以前 だと framework に無いので、NuGet で System.ValueTuple を足す必要があります。
ややこしいのは、4.7 以降でも参照設定や VS の版の都合で、たまに同じエラーが出ること。その時も NuGet で System.ValueTuple を足せば直ります。まずは自分の環境のバージョン確認が先です。
② 名前付きタプルの名前が消える
名前付きの (bool ok, string error) は便利なんですが、この名前、実は コンパイラが付けてくれる糖衣 でしかありません。
なのでリフレクションや dynamic 越し、別アセンブリの境界をまたぐと、名前が Item1 Item2 に戻ることがあります。私も昔、別プロジェクトから受け取ったタプルの .ok が使えなくて「あれ、名前どこいった?」ってなって30分溶かしました。実行時に名前が保証されるわけじゃない、というのは頭の隅に置いておくとよいです。
③ out 引数は EF のクエリ(式ツリー)に書けない
メモリ上の List に対する LINQ なら、out var をラムダの中に書けます。
// メモリ上の List に対してならOK
var oks = names.Where(n => Validate(n, out var _)).ToList();
ところが、これを EF(Entity Framework)の IQueryable に対して書くと、式ツリーに out 引数変数の宣言を含めることはできません(CS8198)というエラーになります。DB 側に投げるクエリは式ツリーに変換されるからです。
DB アクセスと絡めて複数戻り値を扱うなら、out より (bool ok, ...) を返すメソッドにしておく。そうすれば、この制約に引っかかりません。
現場メモ: 戻り値の型は「呼ぶ側の読みやすさ」で決める
戻り値の書き方って、動けばどれでもいい話に見えて、正直、保守で効いてきます。
前に流通系の基幹システムの保守で、集計処理が out 5個で値を返してるメソッドがあって。呼び出し箇所が10箇所以上、どれも out int a, out int b, ... で受けてたんですよね。1個 out を足すたびに、全呼び出し側を直して回る羽目になってました。
これ、最初から名前付き ValueTuple か専用クラスで返してたら、呼ぶ側はほぼ触らずに済んだんです。戻り値の設計は、書く時のラクさより「半年後に読む人・直す人のラクさ」で決める。そう考えると、だいたい外しません。
まとめ
C# で複数の戻り値を返す3つの書き方、こんな感じで覚えておけば十分です。
outは Tryパターンで1個返し に絞る- 日常の2〜3個は 名前付き ValueTuple
- 値が育ってきたら 専用クラス(新しめの環境なら record)
「とりあえず out」で書いてた人は、まず ValueTuple に置き換えるだけで呼ぶ側がだいぶ読みやすくなります。手元のメソッド1個からでも試してみてください。
よくある質問
Q1. ValueTuple と昔の Tuple は何が違いますか?
ValueTuple は構造体(値型)で、Tuple<T> は参照型です。ValueTuple はヒープに乗らないぶん軽くて、(int, string) という構文や名前付き要素が使えます。C# 7 以降は基本 ValueTuple でいいです。
Q2. Framework 4.7.2 で ValueTuple は使えますか?
使えます。System.ValueTuple は .NET Framework 4.7 から framework 標準で同梱されているので、4.7.2 なら NuGet なしでタプル構文が動きます。NuGet が要るのは 4.6.2 以前です。4.7 以降でも参照設定の都合でエラーが出ることが稀にあり、その時は NuGet で System.ValueTuple を足せば直ります。
Q3. out 引数はもう古いですか?
古くないです。int.TryParse のように成功したかと結果をセットで返す用途では、out が今でも一番素直です。返す値が1個で、if で受けたい時に向いてます。
Q4. record を使えばもっと短く書けますか?
書けます。C# 9(.NET 5 以降)が使えるなら、専用クラスを record で1行定義できます。ただ Framework 4.7.2 では record が使えないので、その場合は普通の class でいきましょう。
次に読むべき記事





以上!
同じところでハマってる人がいたら、どんどんシェア待ってるぜ!!




