C#の戻り値とは?returnの使い方・voidとの違い・複数返す方法まで初心者向けに解説

1. C#の戻り値とは?初心者向けにわかりやすく解説

1-1. 戻り値はメソッドの処理結果として呼び出し元に返す値

C#の戻り値とは、メソッドが処理結果として呼び出し元へ返す値のことです。

次のメソッドは、2つの整数を足した結果を返します。

static int Add(int a, int b){return a + b;}

この例では、a + bの計算結果が戻り値です。呼び出し元では、次のように結果を受け取れます。

int total = Add(10, 20);

Console.WriteLine(total); // 30

メソッド内で計算するだけでは、呼び出し元で結果を利用できません。returnを使って値を返すことで、別の処理や計算に利用できるようになります。

1-2. 引数と戻り値の違い

引数と戻り値は、値が渡される方向が異なります。

  • 引数:呼び出し元からメソッドへ渡す値

  • 戻り値:メソッドから呼び出し元へ返す値

次のコードでは、pricequantityが引数であり、掛け算した結果が戻り値です。

static int CalculateTotal(int price, int quantity){return price * quantity;}

int total = CalculateTotal(500, 3);Console.WriteLine(total); // 1500

処理の流れは次のようになります。

  1. 呼び出し元が5003をメソッドへ渡す

  2. メソッドが500 * 3を計算する

  3. 計算結果の1500を呼び出し元へ返す

  4. 呼び出し元が戻り値をtotalへ代入する

引数がなくても戻り値を返すメソッドは作成できます。また、引数があっても値を返さないメソッドも作成できます。

1-3. 戻り値の型とメソッド宣言の関係

戻り値を返すメソッドでは、メソッド名の前に戻り値の型を指定します。

戻り値の型 メソッド名(引数){return 戻り値;}

整数を返す場合はint、文字列を返す場合はstring、真偽値を返す場合はboolを指定します。

static int GetNumber(){return 100;}

static string GetMessage(){return "こんにちは";}

static bool IsAdult(int age){return age >= 18;}

原則として、returnで返す値は、メソッド宣言で指定した戻り値の型と一致している必要があります。

次のコードは、戻り値の型が一致しないためコンパイルエラーになります。

static int GetNumber(){return "100"; // string型なのでエラー}

文字列の"100"と整数の100は、見た目が似ていても異なる型です。

1-4. 戻り値を受け取って変数に代入する方法

メソッドの戻り値は、通常の値と同じように変数へ代入できます。

static double CalculateArea(double width, double height){return width * height;}

double area = CalculateArea(5.5, 3.0);

Console.WriteLine(area); // 16.5

代入先の変数も、戻り値を格納できる型にする必要があります。

double area = CalculateArea(5.5, 3.0);

戻り値を変数へ代入せず、そのまま別のメソッドへ渡すことも可能です。

Console.WriteLine(CalculateArea(5.5, 3.0));

後から何度も使用する場合や、変数名によって意味を明確にしたい場合は、変数へ代入すると読みやすくなります。

2. C#のreturn文の基本的な使い方

2-1. return文の基本構文

return文は、メソッドから値を返すために使用します。

基本構文は次のとおりです。

return 戻り値;

たとえば、固定の整数を返すメソッドは次のように記述します。

static int GetDefaultScore(){return 50;}

returnの後ろには、戻り値の型に対応する値、変数、計算式、メソッドの呼び出し結果などを指定できます。

2-2. int型の値をreturnで返す例

int型は、整数を表す型です。整数を返すメソッドでは、戻り値の型にintを指定します。

static int GetAge(){return 20;}

引数を使って計算した整数を返すこともできます。

static int Subtract(int a, int b){return a - b;}

int result = Subtract(10, 3);

Console.WriteLine(result); // 7

整数の計算結果や件数、点数、番号などを返したい場合にint型がよく使われます。

2-3. string型やbool型の値を返す例

文字列を返す場合は、戻り値の型にstringを指定します。

static string CreateGreeting(string name){return $"こんにちは、{name}さん";}

string message = CreateGreeting("田中");

Console.WriteLine(message);

条件を判定した結果を返す場合は、bool型が便利です。

static bool IsEven(int number){return number % 2 == 0;}

bool result = IsEven(10);

Console.WriteLine(result); // True

bool型の戻り値は、trueまたはfalseのどちらかです。条件分岐と組み合わせることで、処理をわかりやすく分割できます。

if (IsEven(10)){Console.WriteLine("偶数です");}

2-4. 変数・計算結果・条件式をそのまま返す方法

returnでは、変数に入っている値を返せます。

static int CalculateTaxIncludedPrice(int price){int result = (int)(price * 1.1);return result;}

簡単な計算であれば、変数を作らずに計算結果を直接返すことも可能です。

static int CalculateTaxIncludedPrice(int price){return (int)(price * 1.1);}

条件式も直接返せます。

static bool CanEnter(int age){return age >= 18;}

次のように書く必要はありません。

static bool CanEnter(int age){if (age >= 18){return true;}else{return false;}}

単純な条件判定であれば、条件式を直接返したほうが簡潔です。

2-5. returnが実行されるとメソッドの処理が終了する

returnが実行されると、その時点でメソッドの処理が終了し、呼び出し元へ戻ります。

static int GetValue(){Console.WriteLine("returnの前");

return 100;Console.WriteLine("returnの後"); // 実行されない

}

returnより後ろにある到達不能なコードは実行されません。コンパイラによって到達不能コードとして警告やエラーの対象になることがあります。

条件分岐の中でreturnが実行された場合も、メソッド全体が終了します。

static string CheckNumber(int number){if (number < 0){return "負の数です";}

return "0以上です";

}

numberが負の数だった場合、最初のreturnが実行され、その後の処理には進みません。

3. 戻り値ありのメソッドを作成・呼び出す方法

3-1. 戻り値と引数があるメソッドの書き方

戻り値と引数の両方があるメソッドは、次の形式で作成します。

static 戻り値の型 メソッド名(引数の型 引数名){return 戻り値;}

2つの整数のうち、大きいほうを返すメソッドを作成してみましょう。

static int GetLargerNumber(int a, int b){if (a >= b){return a;}

return b;

}

呼び出すときは、必要な引数を指定します。

int larger = GetLargerNumber(15, 8);

Console.WriteLine(larger); // 15

3-2. 戻り値を変数で受け取る方法

戻り値を変数で受け取る基本形は次のとおりです。

データ型 変数名 = メソッド名(引数);

具体例は次のようになります。

static string GetFullName(string lastName, string firstName){return $"{lastName} {firstName}";}

string fullName = GetFullName("山田", "太郎");

Console.WriteLine(fullName);

型をコンパイラに推論させるvarも使用できます。

var fullName = GetFullName("山田", "太郎");

この場合、GetFullNameの戻り値がstringなので、fullNamestring型として扱われます。

3-3. 戻り値を条件分岐や計算で直接使う方法

戻り値は、変数へ代入せずにif文の条件として直接利用できます。

static bool IsValidPassword(string password){return password.Length >= 8;}

if (IsValidPassword("password123")){Console.WriteLine("使用できるパスワードです");}

数値の戻り値を別の計算に利用することも可能です。

static int Add(int a, int b){return a + b;}

int result = Add(10, 20) * 2;

Console.WriteLine(result); // 60

別のメソッドの引数として渡すこともできます。

Console.WriteLine(Add(10, 20));

ただし、メソッド呼び出しを何重にも入れ子にすると読みにくくなることがあります。複雑な処理では、適切な名前の変数へ一度代入すると理解しやすくなります。

3-4. staticメソッドとインスタンスメソッドで戻り値を受け取る例

staticメソッドは、クラスのインスタンスを作成せずに呼び出せます。

class Calculator{public static int Add(int a, int b){return a + b;}}

int result = Calculator.Add(10, 20);

インスタンスメソッドを呼び出すには、通常はクラスのインスタンスを作成します。

class Calculator{public int Multiply(int a, int b){return a * b;}}

Calculator calculator = new Calculator();

int result = calculator.Multiply(4, 5);

Console.WriteLine(result); // 20

戻り値の受け取り方は同じですが、呼び出し方が異なります。

  • staticメソッド:クラス名.メソッド名()

  • インスタンスメソッド:インスタンス名.メソッド名()

3-5. 戻り値の型が一致しない場合に発生するエラー

メソッドで宣言した戻り値の型と、実際に返す値の型が一致しない場合は、コンパイルエラーになることがあります。

static int GetPrice(){return "1000";}

"1000"string型なので、int型として返せません。数値へ変換して返す必要があります。

static int GetPrice(){return int.Parse("1000");}

呼び出し元の変数の型が合わない場合もエラーになります。

static string GetName(){return "佐藤";}

int name = GetName(); // エラー

正しくはstring型で受け取ります。

string name = GetName();

一部の型には暗黙的な変換があります。たとえば、intからlongへの変換は可能です。

static long GetNumber(){int number = 100;return number;}

一方、データが失われる可能性がある変換では、明示的なキャストが必要です。

static int GetNumber(){double number = 10.8;return (int)number;}

この例では小数部分が切り捨てられ、10が返されます。

4. voidとは?戻り値があるメソッドとの違い

4-1. voidは値を返さないメソッドに指定する型

voidは、そのメソッドが値を返さないことを表します。

static void ShowMessage(){Console.WriteLine("メッセージを表示します");}

このメソッドは画面に文字列を表示しますが、呼び出し元へ値を返しません。

そのため、次のように戻り値を変数へ代入することはできません。

string message = ShowMessage(); // エラー

voidは通常のデータ型の値を返すのではなく、「戻り値がない」ことを示す特別な指定です。

4-2. voidメソッドの基本的な書き方

voidメソッドは、戻り値の型を指定する位置にvoidを記述します。

static void メソッド名(引数){処理;}

たとえば、指定された名前を表示するメソッドは次のように書けます。

static void PrintGreeting(string name){Console.WriteLine($"こんにちは、{name}さん");}

PrintGreeting("鈴木");

ファイルへの書き込み、画面表示、オブジェクトの状態変更など、処理の実行自体が目的の場合にvoidが使われます。

4-3. voidメソッドでもreturnだけを記述できる

voidメソッドでは値を返せませんが、return;だけを記述してメソッドを途中で終了できます。

static void PrintPositiveNumber(int number){if (number <= 0){return;}

Console.WriteLine(number);

}

numberが0以下の場合はreturn;が実行され、Console.WriteLineには進みません。

ただし、次のように値を付けることはできません。

static void ShowNumber(){return 10; // エラー}

voidメソッドで使用できるのは、値を伴わないreturn;です。

4-4. 戻り値ありとvoidを使い分ける判断基準

メソッドの処理結果を呼び出し元で利用したい場合は、戻り値ありのメソッドにします。

static int CalculateDiscountPrice(int price){return (int)(price * 0.9);}

画面表示など、処理の実行だけが目的の場合はvoidを選択できます。

static void PrintPrice(int price){Console.WriteLine($"{price}円");}

判断の目安は次のとおりです。

  • 計算結果や取得結果を別の処理で使う:戻り値あり

  • 成功・失敗を呼び出し元へ伝える:boolなどの戻り値あり

  • 表示や通知など、操作の実行だけが目的:void

  • オブジェクトの状態を変更するだけ:状況に応じてvoid

処理結果を返せる設計にすると、メソッドの再利用やテストがしやすくなる場合があります。

4-5. Console.WriteLineがvoidを返す理由

Console.WriteLineは、コンソールへ文字や数値を表示するためのメソッドです。呼び出しの目的は値を取得することではなく、表示という処理を実行することにあります。

そのため、Console.WriteLineの戻り値はvoidです。

Console.WriteLine("こんにちは");

次のように戻り値を受け取ることはできません。

var result = Console.WriteLine("こんにちは"); // エラー

表示する文字列を作る処理と、実際に表示する処理を分けると役割が明確になります。

static string CreateMessage(string name){return $"こんにちは、{name}さん";}

string message = CreateMessage("高橋");Console.WriteLine(message);

CreateMessageは文字列を返し、Console.WriteLineはその文字列を表示します。

5. return文と条件分岐・繰り返し処理の組み合わせ

5-1. if文で条件ごとに異なる値を返す方法

if文とreturnを組み合わせると、条件ごとに異なる値を返せます。

static string GetGrade(int score){if (score >= 80){return "A";}

if (score &gt;= 60){return "B";}return "C";

}

最初に成立した条件のreturnが実行され、その時点でメソッドが終了します。

Console.WriteLine(GetGrade(85)); // AConsole.WriteLine(GetGrade(70)); // BConsole.WriteLine(GetGrade(50)); // C

return後には処理が続かないため、このような書き方ではelseを省略できます。

5-2. 早期returnで不要な処理を減らす方法

メソッドの先頭付近で不正な値や対象外の条件を判定し、早めに処理を終了する書き方を「早期return」と呼びます。

static int CalculateShippingFee(int price){if (price < 0){return 0;}

if (price &gt;= 5000){return 0;}return 500;

}

条件を深く入れ子にするより、早期returnを使ったほうが読みやすくなることがあります。

static string RegisterUser(string name){if (string.IsNullOrWhiteSpace(name)){return "名前を入力してください";}

if (name.Length &gt; 20){return "名前は20文字以内で入力してください";}return "登録しました";

}

エラー条件を先に処理し、最後に正常時の結果を返すのが代表的な使い方です。

5-3. switch式を使って値を返す方法

複数の条件から値を選ぶ場合は、switch式を使うと簡潔に書けます。

static string GetDayType(DayOfWeek day){return day switch{DayOfWeek.Saturday => "休日",DayOfWeek.Sunday => "休日",_ => "平日"};}

switch式自体が値を生成するため、その結果をそのままreturnできます。

数値の範囲を判定することも可能です。

static string GetRank(int score){return score switch{>= 90 => "S",>= 80 => "A",>= 60 => "B",_ => "C"};}

単純な条件分岐で戻り値を選ぶ場合、通常のswitch文よりコードを短くできることがあります。

5-4. for文やforeach文の途中でreturnする場合の動作

繰り返し処理の中でreturnを実行すると、ループだけでなくメソッド全体が終了します。

static int FindFirstEvenNumber(int[] numbers){foreach (int number in numbers){if (number % 2 == 0){return number;}}

return -1;

}

最初の偶数が見つかった時点で、その値を返してメソッドを終了します。

int[] numbers = { 1, 3, 8, 10 };

int result = FindFirstEvenNumber(numbers);

Console.WriteLine(result); // 8

for文でも動作は同じです。

static int FindIndex(int[] numbers, int target){for (int i = 0; i < numbers.Length; i++){if (numbers[i] == target){return i;}}

return -1;

}

対象が見つからなかった場合に返す値も決めておく必要があります。

5-5. すべての処理経路で戻り値を返す必要がある理由

戻り値ありのメソッドでは、どの条件を通った場合でも最終的に値を返す必要があります。

次のコードは、scoreが60未満だった場合に戻り値がないためエラーになります。

static string CheckResult(int score){if (score >= 60){return "合格";}}

すべての処理経路で値を返すように修正します。

static string CheckResult(int score){if (score >= 60){return "合格";}

return "不合格";

}

または、ifelseの両方で返すことも可能です。

static string CheckResult(int score){if (score >= 60){return "合格";}else{return "不合格";}}

呼び出し元は必ず戻り値を受け取ることを前提としているため、値が返されない可能性を残すことはできません。

6. C#で複数の戻り値を返す方法

6-1. タプルを使って複数の値を返す方法

通常、メソッドの戻り値は1つです。ただし、タプルを1つの戻り値として使えば、複数の値をまとめて返せます。

static (string, int) GetUser(){return ("田中", 25);}

呼び出し元では、Item1Item2で値を参照できます。

var user = GetUser();

Console.WriteLine(user.Item1); // 田中Console.WriteLine(user.Item2); // 25

タプルは、関連する少数の値を手軽にまとめて返したい場合に便利です。

6-2. 名前付きタプルで戻り値をわかりやすくする方法

タプルの各要素には名前を付けられます。

static (string Name, int Age) GetUser(){return ("田中", 25);}

呼び出し元では、指定した名前で値へアクセスできます。

var user = GetUser();

Console.WriteLine(user.Name);Console.WriteLine(user.Age);

Item1Item2よりも、NameAgeのような名前を使ったほうが値の意味を理解しやすくなります。

戻り値を作るときにも要素名を記述できます。

static (int Min, int Max) GetRange(int a, int b){return a <= b? (Min: a, Max: b): (Min: b, Max: a);}

6-3. 分解代入でタプルの戻り値を受け取る方法

タプルの戻り値は、分解代入を使って複数の変数へ直接代入できます。

static (string Name, int Age) GetUser(){return ("田中", 25);}

(string name, int age) = GetUser();

Console.WriteLine(name);Console.WriteLine(age);

型推論を使う場合は、次のように書けます。

var (name, age) = GetUser();

一部の値が不要な場合は、破棄を表す_を使用できます。

var (name, _) = GetUser();

Console.WriteLine(name);

分解代入を使うと、それぞれの値をすぐに個別の変数として扱えます。

6-4. out引数を使って複数の値を返す方法

out引数を使うと、通常の戻り値とは別に呼び出し元へ値を渡せます。

static bool TryDivide(int dividend,int divisor,out double result){if (divisor == 0){result = 0;return false;}

result = (double)dividend / divisor;return true;

}

呼び出し元では、outを付けて結果を受け取ります。

bool success = TryDivide(10, 2, out double result);

if (success){Console.WriteLine(result); // 5}

変数宣言と呼び出しを分けることもできます。

double result;bool success = TryDivide(10, 2, out result);

out引数を持つメソッドでは、正常終了するすべての処理経路でout引数へ値を代入しなければなりません。

6-5. クラスやrecordを戻り値として使う方法

返すデータが多い場合や、データに明確な意味を持たせたい場合は、専用のクラスやrecordを作成します。

public record UserResult(int Id,string Name,string Email);

作成したrecordを戻り値の型として使用できます。

static UserResult GetUser(){return new UserResult(1,"田中","tanaka@example.com");}

呼び出し元では、プロパティ名で値へアクセスします。

UserResult user = GetUser();

Console.WriteLine(user.Id);Console.WriteLine(user.Name);Console.WriteLine(user.Email);

専用の型を作ると、各値の意味が明確になり、後からプロパティを追加しやすくなります。

6-6. 配列やListで複数の同じ型の値を返す方法

同じ型の値を複数返す場合は、配列やList<T>が適しています。

配列を返す例は次のとおりです。

static int[] GetScores(){return new[] { 80, 90, 75 };}

int[] scores = GetScores();

List<T>を返す例は次のとおりです。

static List<string> GetNames(){return new List<string>{"田中","佐藤","鈴木"};}

コレクションの内容を変更させたくない場合は、用途に応じてIReadOnlyList<T>などを戻り値にする方法もあります。

static IReadOnlyList<string> GetNames(){return new List<string>{"田中","佐藤","鈴木"};}

6-7. タプル・out・クラス・コレクションの使い分け

複数の値を返す方法は、データの性質に応じて使い分けます。

タプルは、関連する少数の値を一時的にまとめたい場合に向いています。

(string Name, int Age)

outは、処理の成功・失敗と結果を同時に受け取るTryパターンなどでよく使われます。

bool TryParse(string text, out int result)

クラスやrecordは、項目数が多い場合や、その結果を複数の場所で利用する場合に適しています。

UserResult

配列やList<T>は、同じ型の値を0件以上返す場合に適しています。

List<string>

選択の目安は次のとおりです。

  • 少数の異なる値を手軽に返す:タプル

  • 成功・失敗と結果を返す:Tryパターンとout

  • 意味のある複雑なデータを返す:クラスまたはrecord

  • 同じ型のデータを複数返す:配列やコレクション

7. 戻り値としてnullやNullable型を返す方法

7-1. 参照型の戻り値でnullを返す場合

検索対象が見つからなかったことを示すために、参照型の戻り値としてnullを返す場合があります。

static string? FindName(int id){if (id == 1){return "田中";}

return null;

}

nullは、参照先のオブジェクトが存在しないことを表します。

ただし、戻り値がnullかもしれない場合、呼び出し元では適切な確認が必要です。

string? name = FindName(2);

if (name is not null){Console.WriteLine(name);}

7-2. Nullable参照型を戻り値に指定する方法

Nullable参照型が有効な環境では、string?のように?を付けることで、nullを返す可能性を明示できます。

#nullable enable

static string? FindUserName(int id){if (id == 1){return "佐藤";}

return null;

}

stringstring?の意味は次のように異なります。

  • string:原則としてnullではない

  • string?nullの可能性がある

呼び出し元では、コンパイラの警告を参考にしながらnullチェックを行えます。

string? name = FindUserName(2);

Console.WriteLine(name?.Length);

?.は、対象がnullでなければメンバーへアクセスし、nullならそのままnullを返す演算子です。

7-3. int?などのNullable値型を返す方法

intboolなどの値型は、通常はnullを格納できません。int?のように?を付けると、値またはnullを扱えます。

static int? FindScore(string name){if (name == "田中"){return 90;}

return null;

}

呼び出し元では、HasValueで値の有無を確認できます。

int? score = FindScore("田中");

if (score.HasValue){Console.WriteLine(score.Value);}

パターンマッチングを使うと、より簡潔に書けます。

if (score is int value){Console.WriteLine(value);}

値がない場合の既定値を指定するには、null合体演算子??を使用できます。

int displayScore = score ?? 0;

7-4. nullの代わりにTryパターンを使う方法

処理が成功したかどうかを明確に表したい場合は、boolout引数を組み合わせたTryパターンを利用できます。

代表例はint.TryParseです。

bool success = int.TryParse("123", out int number);

if (success){Console.WriteLine(number);}

独自のTryメソッドも作成できます。

static bool TryFindScore(string name,out int score){if (name == "田中"){score = 90;return true;}

score = 0;return false;

}

呼び出し元では、成功した場合だけ結果を利用できます。

if (TryFindScore("田中", out int score)){Console.WriteLine(score);}

Tryパターンは、値がない状態と、値としての0などを区別したい場合にも便利です。

7-5. 戻り値のnullチェックで例外を防ぐ方法

nullの可能性がある戻り値へ、そのままメンバーアクセスすると、NullReferenceExceptionが発生する可能性があります。

string? name = FindName(2);

Console.WriteLine(name.Length);

安全に扱うには、nullチェックを行います。

if (name is not null){Console.WriteLine(name.Length);}

null条件演算子?.も利用できます。

Console.WriteLine(name?.Length);

既定値を使いたい場合は??を組み合わせます。

int length = name?.Length ?? 0;

文字列そのものに既定値を設定することもできます。

string displayName = name ?? "名前なし";

戻り値がnullになり得る設計では、呼び出し元がその状態を見落とさないように、string?などで明示することが重要です。

8. 非同期メソッドの戻り値TaskとTaskの違い

8-1. asyncメソッドで値を返さない場合はTaskを使う

非同期メソッドが値を返さない場合は、戻り値の型としてTaskを使用します。

static async Task ShowMessageAsync(){await Task.Delay(1000);Console.WriteLine("処理が完了しました");}

呼び出し元ではawaitを付けます。

await ShowMessageAsync();

Taskは非同期処理の進行状況や完了を表します。通常の同期メソッドにおけるvoidに近い役割ですが、呼び出し元が処理の完了を待機したり、例外を受け取ったりできます。

8-2. asyncメソッドで値を返す場合はTaskを使う

非同期処理の結果として値を返す場合は、Task<T>を使用します。

整数を返す場合はTask<int>です。

static async Task<int> GetNumberAsync(){await Task.Delay(1000);return 100;}

文字列を返す場合はTask<string>です。

static async Task<string> GetMessageAsync(){await Task.Delay(1000);return "完了";}

asyncメソッド内のreturnでは、Task<T>そのものではなく、Tに対応する値を返します。

static async Task<int> GetNumberAsync(){await Task.Delay(100);return 10;}

この例では戻り値の宣言はTask<int>ですが、returnする値はint型の10です。

8-3. awaitで非同期処理の戻り値を受け取る方法

Task<T>を返す非同期メソッドの結果は、awaitを使って受け取ります。

static async Task<int> CalculateAsync(){await Task.Delay(500);return 10 + 20;}

int result = await CalculateAsync();

Console.WriteLine(result); // 30

awaitを使うと、Task<int>の処理完了後に得られるint型の値を受け取れます。

awaitせずに呼び出すと、受け取るのは計算結果ではなくTask<int>です。

Task<int> task = CalculateAsync();

後から待機して結果を受け取ることもできます。

int result = await task;

8-4. async voidを原則として避ける理由

非同期メソッドでも、次のようにasync voidを記述できます。

static async void RunAsync(){await Task.Delay(1000);}

しかし、async voidは呼び出し元が処理の完了を待機できず、例外も扱いにくいため、原則として避けます。

通常はTaskを返します。

static async Task RunAsync(){await Task.Delay(1000);}

Taskであれば、呼び出し元はawaitできます。

await RunAsync();

async voidが必要になる代表的な場面は、GUIアプリケーションなどのイベントハンドラーです。

private async void Button_Click(object sender,EventArgs e){await Task.Delay(1000);}

イベントハンドラー以外の非同期メソッドでは、基本的にTaskまたはTask<T>を選びます。

9. C#の戻り値でよくあるエラーと解決方法

9-1. 「すべてのコード パスが値を返すわけではありません」の原因

戻り値ありのメソッドで、値を返さずに終了する可能性があると、「すべてのコード パスが値を返すわけではありません」というエラーが発生します。

static int GetDiscount(int price){if (price >= 5000){return 500;}}

priceが5000未満の場合に戻り値がありません。

すべての条件で値を返すように修正します。

static int GetDiscount(int price){if (price >= 5000){return 500;}

return 0;

}

条件分岐が複雑な場合は、各経路をたどって、必ずreturnまたは例外のスローに到達するか確認しましょう。

static int GetValue(bool isValid){if (!isValid){throw new ArgumentException("無効な値です");}

return 100;

}

例外をスローした経路は、通常の戻り値を返さずにメソッドが終了するため、別途returnは必要ありません。

9-2. 戻り値の型を暗黙的に変換できないエラー

宣言した戻り値の型へ変換できない値を返すと、型変換に関するエラーが発生します。

static int GetNumber(){return "10";}

必要に応じて変換処理を行います。

static int GetNumber(){return int.Parse("10");}

外部入力など、変換に失敗する可能性がある文字列ではTryParseが安全です。

static int GetNumber(string text){if (int.TryParse(text, out int number)){return number;}

return 0;

}

小数から整数へ変換する場合は明示的なキャストが必要です。

static int GetInteger(){double value = 10.9;return (int)value;}

ただし、キャストによって小数部分が失われる点に注意が必要です。

9-3. voidメソッドから値を返そうとした場合のエラー

voidメソッドから値を返すことはできません。

static void Add(int a, int b){return a + b;}

計算結果を返したい場合は、戻り値の型を変更します。

static int Add(int a, int b){return a + b;}

値を返す必要がなく、途中で処理を終了したいだけなら、値を付けずにreturn;を使います。

static void PrintNumber(int number){if (number < 0){return;}

Console.WriteLine(number);

}

9-4. returnの後に処理を書いた場合の到達不能コード

無条件に実行されるreturnの後ろに書いた処理には到達できません。

static int GetNumber(){return 10;

Console.WriteLine("実行されません");

}

returnの後に必要な処理を書いていた場合は、順序を入れ替えます。

static int GetNumber(){Console.WriteLine("値を返します");

return 10;

}

条件付きのreturnであれば、条件が成立しなかった場合に後続処理へ進めます。

static int GetNumber(bool useDefault){if (useDefault){return 0;}

Console.WriteLine("通常の値を返します");return 10;

}

9-5. nullの戻り値によるNullReferenceException

戻り値がnullであるにもかかわらず、プロパティやメソッドへアクセスするとNullReferenceExceptionが発生する可能性があります。

static string? FindName(){return null;}

string? name = FindName();

Console.WriteLine(name.Length);

nullチェックを追加します。

if (name is not null){Console.WriteLine(name.Length);}

または、null条件演算子を利用します。

Console.WriteLine(name?.Length);

必ず値が必要な場合は、既定値を指定できます。

string displayName = FindName() ?? "不明";

nullを返す可能性があるメソッドでは、戻り値の型に?を付け、呼び出し側へ可能性を伝えることが大切です。

10. 読みやすい戻り値設計のポイント

10-1. メソッド名から戻り値の内容がわかるようにする

戻り値を返すメソッドには、何が返されるのか想像しやすい名前を付けます。

たとえば、次の名前だけでは戻り値の内容がわかりにくい場合があります。

int Process()

目的を表す名前にすると、戻り値の意味が明確になります。

int CalculateTotalPrice()int GetUserCount()bool IsAvailable()string CreateDisplayName()

一般的な命名の例は次のとおりです。

  • 値を取得する:Get...

  • 値を計算する:Calculate...

  • 文字列などを生成する:Create...

  • 条件を判定する:Is...Has...Can...

  • 成功・失敗を伴う取得:TryGet...

メソッド名、引数、戻り値を見ただけで役割を理解できる設計を目指しましょう。

10-2. 戻り値の型を目的に合わせて選ぶ

戻り値の型は、返したい情報を適切に表現できるものを選びます。

件数を返すならintが適しています。

int GetItemCount()

小数を含む計算結果ならdoubledecimalを検討します。

decimal CalculateTotalPrice()

成功・失敗だけを返すならboolが利用できます。

bool IsValid()

複数の項目を持つ結果なら、専用のクラスやrecordが適しています。

OrderResult CreateOrder()

公開するメソッドでは、具体的な実装より抽象的なインターフェースを戻り値にすることもあります。

IReadOnlyList<string> GetNames()

目的に合った型を選ぶことで、呼び出し元が戻り値を誤って扱う可能性を減らせます。

10-3. boolだけでは結果がわかりにくい場合の改善方法

boolは成功か失敗かを表すには便利ですが、失敗理由までは伝えられません。

static bool RegisterUser(string name){// 登録処理return false;}

falseだけでは、名前が空なのか、重複しているのか、データベースでエラーが起きたのか判断できません。

結果を列挙型で返す方法があります。

enum RegistrationResult{Success,NameIsEmpty,AlreadyExists}
static RegistrationResult RegisterUser(string name){if (string.IsNullOrWhiteSpace(name)){return RegistrationResult.NameIsEmpty;}

return RegistrationResult.Success;

}

より多くの情報が必要なら、専用の結果型を作成します。

public record OperationResult(bool IsSuccess,string? ErrorMessage);
static OperationResult RegisterUser(string name){if (string.IsNullOrWhiteSpace(name)){return new OperationResult(false,"名前を入力してください");}

return new OperationResult(true, null);

}

10-4. 複雑なデータは専用クラスやrecordで返す

項目数が多いデータをタプルで返すと、メソッドの役割や各項目の意味がわかりにくくなることがあります。

(string, int, string, bool) GetUser()

専用の型を定義すると明確になります。

public record UserInfo(string Name,int Age,string Email,bool IsActive);
static UserInfo GetUser(){return new UserInfo("田中",25,"tanaka@example.com",true);}

専用の型には次の利点があります。

  • 戻り値の意味が明確になる

  • プロパティを名前で参照できる

  • 複数のメソッドで再利用できる

  • 後から項目や処理を整理しやすい

  • XMLドキュメントコメントなどを追加しやすい

一時的な少数の値にはタプル、ドメイン上の意味を持つデータには専用型という使い分けが有効です。

10-5. 副作用を減らして戻り値で結果を受け渡す

外部の変数やオブジェクトの状態を直接変更する処理は、副作用と呼ばれます。副作用が多いメソッドは、実行後の状態を追いにくくなることがあります。

外部変数を直接変更する例は次のとおりです。

static int total;

static void Calculate(int a, int b){total = a + b;}

戻り値で結果を返すと、値の流れが明確になります。

static int Calculate(int a, int b){return a + b;}

int total = Calculate(10, 20);

戻り値を中心に設計すると、同じ引数に対して同じ結果を返すメソッドを作りやすくなります。そのようなメソッドは動作を予測しやすく、単体テストも行いやすくなります。

ただし、画面表示やファイル保存など、副作用そのものが目的となる処理もあります。すべてを戻り値だけで設計するのではなく、計算処理と外部操作を分離することがポイントです。

11. C#の戻り値に関するよくある質問

11-1. C#のメソッドは必ず戻り値が必要?

すべてのメソッドが値を返す必要はありません。値を返さないメソッドにはvoidを指定します。

static void ShowMessage(){Console.WriteLine("こんにちは");}

ただし、メソッド宣言では、戻り値の型またはvoidのどちらかを必ず指定します。

値を返すメソッドでは、宣言した型に対応する値をreturnで返します。

static string GetMessage(){return "こんにちは";}

11-2. returnを複数回書いても問題ない?

1つのメソッド内にreturnを複数回書いても問題ありません。

static string CheckAge(int age){if (age < 0){return "年齢が不正です";}

if (age &lt; 18){return "未成年です";}return "成人です";

}

実行時には、最初に到達したreturnだけが実行されます。

複数のreturnを使うと条件ごとの処理が明確になる場合があります。一方で、非常に長いメソッドに多数のreturnがあると流れを追いにくくなるため、メソッドを小さく分割することも検討しましょう。

11-3. returnで複数の値を直接返せる?

returnが返す戻り値は1つです。ただし、タプルやクラス、配列などに複数の値をまとめれば、実質的に複数の値を返せます。

タプルを使う例は次のとおりです。

static (int Sum, int Product) Calculate(int a,int b){return (a + b, a * b);}
var (sum, product) = Calculate(3, 4);

Console.WriteLine(sum); // 7Console.WriteLine(product); // 12

同じ型の複数データなら、配列やList<T>も利用できます。

11-4. voidとnullは同じ意味?

voidnullは異なります。

voidは、メソッドが値を返さないことを表します。

static void ShowMessage(){Console.WriteLine("表示");}

nullは、参照先のオブジェクトや値が存在しない状態を表す値です。

static string? FindName(){return null;}

FindNameにはstring?という戻り値があります。その戻り値としてnullを返しています。

一方、voidメソッドには受け取れる戻り値自体がありません。

11-5. returnとbreak・continueの違いは?

returnbreakcontinueは、処理の流れを変更する範囲が異なります。

returnはメソッド全体を終了します。

static void Run(){for (int i = 0; i < 10; i++){if (i == 5){return;}

    Console.WriteLine(i);}Console.WriteLine("ここには到達しません");

}

breakは、現在のループまたはswitchを終了します。メソッド自体は続きます。

static void Run(){for (int i = 0; i < 10; i++){if (i == 5){break;}

    Console.WriteLine(i);}Console.WriteLine("ループ終了");

}

continueは、現在の繰り返しの残りを飛ばし、次の繰り返しへ進みます。

for (int i = 0; i < 5; i++){if (i == 2){continue;}

Console.WriteLine(i);

}

使い分けは次のとおりです。

  • return:メソッドを終了する

  • break:ループまたはswitchを終了する

  • continue:現在の繰り返しだけを終了する

11-6. Mainメソッドの戻り値には何を指定する?

C#のMainメソッドでは、主にvoidまたはintを戻り値として指定できます。

値を返さない例は次のとおりです。

static void Main(string[] args){Console.WriteLine("プログラムを開始します");}

intを返す場合は、終了コードとして利用されます。

static int Main(string[] args){Console.WriteLine("正常終了");return 0;}

一般的に、0は正常終了を表し、0以外の値は何らかのエラーを表すために使われます。

非同期処理を行う場合は、TaskまたはTask<int>も利用できます。

static async Task Main(string[] args){await Task.Delay(1000);Console.WriteLine("完了");}

終了コードを返す非同期のMainメソッドは、次のように記述できます。

static async Task<int> Main(string[] args){await Task.Delay(1000);return 0;}

トップレベルステートメントを使用する場合は、Mainメソッドを明示的に記述せずにプログラムを作成することもできます。

まとめ

C#の戻り値は、メソッドの処理結果を呼び出し元へ返すための仕組みです。戻り値を返すメソッドでは、メソッド名の前にintstringboolなどの型を指定し、return文でその型に対応する値を返します。

値を返さないメソッドにはvoidを指定します。voidメソッドでもreturn;を使って途中終了できますが、値を付けて返すことはできません。

複数の値を返したい場合は、用途に応じてタプル、out引数、クラス、record、配列、List<T>などを利用できます。少数の一時的な値にはタプル、複雑で意味のある結果には専用のクラスやrecordが適しています。

また、戻り値としてnullを返す場合は、Nullable参照型やNullable値型を使用し、呼び出し元でnullチェックを行うことが重要です。非同期メソッドでは、値を返さない場合にTask、値を返す場合にTask<T>を使用します。

戻り値を適切に設計すると、値の流れが明確になり、メソッドの再利用性や読みやすさ、テストのしやすさが向上します。まずはintstringを返す簡単なメソッドから作成し、タプルや非同期処理などへ段階的に理解を広げていきましょう。