【C#】メソッドから異なる型の戻り値を2つ返すベストプラクティス(テスト実装例付き)
🧩 背景
C#で「メソッドから異なる型の2つの値を返したい」という場面に遭遇したことはありませんか?
たとえば、以下のように「保存処理のレスポンス」と「実際にDBへ登録されたエンティティ」を同時に返したいケースです。
var response = await service.StoreAsync(request);
var actual = await dbContext.Foo.SingleOrDefaultAsync();
この2つをメソッドからまとめて返すにはどうすればよいでしょうか?
この記事では、タプルと専用DTO/レコード型の2つの方法でのベストプラクティスを紹介します。
✅ 方法1. タプル (T1, T2)
を使う(簡潔で便利)
最も手軽に「2つの戻り値」を扱うには、タプルが便利です。
private async Task<(ResponseDto Response, Foo? Actual)> ExecuteStoreAsync(...)
{
var response = await service.StoreAsync(request);
var actual = await dbContext.Foo.SingleOrDefaultAsync();
return (response, actual);
}
呼び出し側の使用例
var (response, actual) = await ExecuteStoreAsync(...);
Assert.NotNull(response);
Assert.NotNull(actual);
👍 メリット
- 実装がとてもシンプル
- テストコード内での一時的な利用に最適
👎 デメリット
- 意味が不明瞭になりやすい(
Item1
,Item2
に見えることも) - 戻り値が増えると混乱のもとになる
🔧 方法2. 専用のDTOやレコード型を定義する(拡張性&可読性◎)
タプルよりも少し構造が増えますが、拡張性や可読性を考えるとベストな方法です。
public record ExecuteStoreResult(ResponseDto Response, Foo? Actual);
private async Task<ExecuteStoreResult> ExecuteStoreAsync(...)
{
var response = await service.StoreAsync(request);
var actual = await dbContext.Foo.SingleOrDefaultAsync();
return new ExecuteStoreResult(response, actual);
}
呼び出し側の使用例
var result = await ExecuteStoreAsync(...);
Assert.Equal("ExpectedName", result.Actual?.Name);
Assert.Equal(Status.Success, result.Response.Status);
👍 メリット
- プロパティ名で意味が明確
- 今後プロパティを追加しやすく、可読性も高い
- 本番コードでも違和感なく使える
👎 デメリット
- DTO/レコードを定義する手間は若干ある
🚀 どちらを選ぶべき?
条件 | 推奨方法 |
---|---|
テストなど短期的・限定的に使う | ✅ タプル |
プロダクションコードや拡張を見越す | ✅ DTO / レコード |
❌ NGパターン:objectやdynamicで返す
以下のように object
や dynamic
を使うと型安全が失われ、非常に危険です。
object GetData() => return someCondition ? (object)"文字列" : 123;
これはテストでも本番コードでも非推奨です。
✅ まとめ
-
戻り値を複数返したいとき、C#では
タプル
またはDTO/レコード
を使おう。 - タプルは短期用途向け、DTO/レコードは長期・本番用途向け。
-
object
やdynamic
は極力使わない方がよい。
🙋♂️補足
この記事では以下のような実装シーンを元にしています:
var response = await service.StoreAsync(request);
var actualMBD = await GetMaintenanceBuildingDataAsync(dbContext, request.ConstructionNumber);
テストコードでこのように2つの型を扱いたくなったら、ぜひこの記事の手法を活用してみてください!
📌 関連タグ
#CSharp
#ユニットテスト
#設計
#DTO
#Tips
👍 この記事が役に立ったら、いいね・ストックいただけると励みになります!