C#のオプション引数とは?使い方・デフォルト値・注意点を初心者向けに徹底解説
はじめに
C#でメソッドを作っていると、「この引数は毎回指定しなくてもよいのでは?」と感じることがあります。たとえば、ログを出力するメソッドで、通常はログレベルを「Info」にしたいけれど、必要なときだけ「Error」や「Debug」を指定したい場合です。
このような場面で便利なのが、C#のオプション引数です。
オプション引数を使うと、メソッドを呼び出すときに一部の引数を省略できます。省略された引数には、あらかじめ決めておいたデフォルト値が自動的に使われます。
この記事では、C#のオプション引数とは何か、基本的な書き方、デフォルト値のルール、名前付き引数との組み合わせ、注意点、オーバーロードとの違いまで、初心者向けにわかりやすく解説します。
1. C#のオプション引数とは?
1-1. オプション引数の基本的な意味
C#のオプション引数とは、メソッドを呼び出すときに省略できる引数のことです。
通常、メソッドに引数が定義されている場合、呼び出し側ではその引数に対応する値を必ず渡す必要があります。しかし、オプション引数を使うと、引数にあらかじめデフォルト値を設定できるため、呼び出し時に値を渡さなくてもメソッドを実行できます。
たとえば、次のようなメソッドを考えてみます。
C#void ShowMessage(string message = "こんにちは")
{
Console.WriteLine(message);
}
このメソッドでは、messageに"こんにちは"というデフォルト値が設定されています。そのため、次のように引数を省略して呼び出すことができます。
C#ShowMessage();
この場合、画面には次のように表示されます。
C#こんにちは
もちろん、値を指定して呼び出すこともできます。
C#ShowMessage("おはようございます");
この場合は、指定した値が使われます。
C#おはようございます
1-2. 引数を省略できる仕組み
オプション引数を省略できるのは、メソッド定義側で引数にデフォルト値を設定しているからです。
C#void PrintName(string name = "未設定")
{
Console.WriteLine(name);
}
このように、引数名の後ろに= 値を記述すると、その引数はオプション引数になります。
C#PrintName();
上記のように呼び出すと、C#は省略されたnameに対して、デフォルト値の"未設定"を使います。
つまり、次の呼び出しと同じような意味になります。
C#PrintName("未設定");
ただし、内部的には少し注意が必要です。C#のオプション引数のデフォルト値は、呼び出し側のコードにコンパイル時に埋め込まれます。そのため、後からデフォルト値を変更した場合には、呼び出し側を再コンパイルしないと変更が反映されないことがあります。
この点は、特にライブラリやpublicなAPIを作るときに重要です。
1-3. 通常の引数との違い
通常の引数とオプション引数の大きな違いは、呼び出し時に省略できるかどうかです。
通常の引数は、必ず値を渡す必要があります。
C#void Greet(string name)
{
Console.WriteLine($"こんにちは、{name}さん");
}
Greet("田中");
このメソッドを次のように呼び出すとエラーになります。
C#Greet();
nameに渡す値がないためです。
一方、オプション引数を使うと、値を省略できます。
C#void Greet(string name = "ゲスト")
{
Console.WriteLine($"こんにちは、{name}さん");
}
Greet();
実行結果は次のようになります。
C#こんにちは、ゲストさん
通常の引数は「必ず指定してほしい値」に使い、オプション引数は「指定されなければ標準の値を使えばよい値」に使うと考えるとわかりやすいです。
1-4. オプション引数が役立つ場面
オプション引数は、次のような場面で役立ちます。
たとえば、ログ出力メソッドで、通常はログレベルをInfoにしたい場合です。
C#void WriteLog(string message, string level = "Info")
{
Console.WriteLine($"[{level}] {message}");
}
通常のログであれば、次のように簡単に呼び出せます。
C#WriteLog("処理を開始しました");
必要なときだけ、ログレベルを指定します。
C#WriteLog("エラーが発生しました", "Error");
また、検索処理でページ番号や件数に標準値を設定したい場合にも便利です。
C#void Search(string keyword, int page = 1, int pageSize = 20)
{
Console.WriteLine($"キーワード: {keyword}");
Console.WriteLine($"ページ: {page}");
Console.WriteLine($"件数: {pageSize}");
}
このように、毎回指定する必要がない値に対してオプション引数を使うと、メソッド呼び出しを簡潔に書けます。
2. C#のオプション引数の基本的な書き方
2-1. メソッドでオプション引数を定義する構文
C#でオプション引数を定義する基本構文は次のとおりです。
C#戻り値の型 メソッド名(型 引数名 = デフォルト値)
{
// 処理
}
具体例を見てみましょう。
C#void DisplayMessage(string message = "メッセージはありません")
{
Console.WriteLine(message);
}
この例では、messageがオプション引数です。呼び出し時にmessageを省略すると、"メッセージはありません"が使われます。
C#DisplayMessage();
実行結果は次のとおりです。
C#メッセージはありません
値を指定することもできます。
C#DisplayMessage("登録が完了しました");
実行結果は次のとおりです。
C#登録が完了しました
2-2. デフォルト値を指定する方法
オプション引数では、引数の宣言時に=を使ってデフォルト値を指定します。
C#void PrintUser(string name = "ゲスト", int age = 0)
{
Console.WriteLine($"名前: {name}");
Console.WriteLine($"年齢: {age}");
}
このように、複数のオプション引数を定義することもできます。
C#PrintUser();
実行結果は次のようになります。
C#名前: ゲスト
年齢: 0
nameだけを指定した場合は、ageにはデフォルト値が使われます。
C#PrintUser("佐藤");
実行結果は次のようになります。
C#名前: 佐藤
年齢: 0
両方指定することもできます。
C#PrintUser("佐藤", 30);
実行結果は次のようになります。
C#名前: 佐藤
年齢: 30
2-3. オプション引数を省略して呼び出す例
オプション引数は、省略して呼び出せることが大きな特徴です。
C#void ShowPrice(int price, bool includeTax = true)
{
if (includeTax)
{
Console.WriteLine($"税込価格: {price * 1.1}");
}
else
{
Console.WriteLine($"税抜価格: {price}");
}
}
このメソッドでは、priceは必須引数、includeTaxはオプション引数です。
C#ShowPrice(1000);
includeTaxを省略しているため、デフォルト値のtrueが使われます。
実行結果は次のようになります。
C#税込価格: 1100
このように、よく使う値をデフォルト値として設定しておくと、呼び出し側のコードを短くできます。
2-4. オプション引数に値を指定して呼び出す例
オプション引数は、省略できるだけでなく、必要に応じて値を指定することもできます。
C#ShowPrice(1000, false);
この場合、includeTaxにはfalseが渡されます。
実行結果は次のようになります。
C#税抜価格: 1000
つまり、オプション引数は「必ず省略しなければならない引数」ではありません。
普段は省略できるけれど、必要なときには明示的に指定できる引数です。
3. デフォルト値の指定ルール
3-1. デフォルト値に使える値
C#のオプション引数に指定できるデフォルト値にはルールがあります。
基本的には、コンパイル時に値が確定する定数を指定する必要があります。
たとえば、次のような値はデフォルト値として使えます。
C#void Sample(
int count = 10,
double rate = 1.5,
bool enabled = true,
string name = "ゲスト",
string? memo = null
)
{
}
数値、文字列、bool、nullなどはよく使われます。
一方で、通常の変数や実行時に計算される値は、デフォルト値として指定できません。
C#int defaultCount = 10;
// これはエラー
void Sample(int count = defaultCount)
{
}
このように書くと、コンパイルエラーになります。
3-2. 文字列・数値・bool・nullの指定例
文字列をデフォルト値にする例です。
C#void SayHello(string name = "ゲスト")
{
Console.WriteLine($"こんにちは、{name}さん");
}
数値をデフォルト値にする例です。
C#void SetVolume(int volume = 50)
{
Console.WriteLine($"音量: {volume}");
}
boolをデフォルト値にする例です。
C#void SaveData(bool overwrite = false)
{
Console.WriteLine($"上書き保存: {overwrite}");
}
nullをデフォルト値にする例です。
C#void PrintMemo(string? memo = null)
{
if (memo == null)
{
Console.WriteLine("メモはありません");
}
else
{
Console.WriteLine(memo);
}
}
nullをデフォルト値にすると、「値が指定されなかった場合」と「明示的にnullが指定された場合」を同じように扱う設計にできます。
3-3. 変数や式をデフォルト値にできない理由
オプション引数のデフォルト値には、基本的にコンパイル時に確定できる値を指定する必要があります。
そのため、次のようなコードは書けません。
C#int defaultAge = 20;
// エラー
void ShowAge(int age = defaultAge)
{
Console.WriteLine(age);
}
defaultAgeは変数であり、実行時に値が変わる可能性があります。C#のオプション引数では、このような値をデフォルト値にすることはできません。
また、次のような実行時に評価される式も使えません。
C#// エラー
void ShowTime(DateTime time = DateTime.Now)
{
Console.WriteLine(time);
}
DateTime.Nowはプログラム実行時の現在日時を返すため、コンパイル時に値が確定しません。
このような場合は、デフォルト値にnullを使い、メソッドの中で値を設定する方法がよく使われます。
C#void ShowTime(DateTime? time = null)
{
DateTime actualTime = time ?? DateTime.Now;
Console.WriteLine(actualTime);
}
この方法であれば、引数が省略されたときにメソッド内部で現在日時を設定できます。
3-4. デフォルト値を変更するときの注意点
オプション引数のデフォルト値を後から変更するときは注意が必要です。
たとえば、ライブラリ側に次のようなメソッドがあるとします。
C#public void Send(string message, bool urgent = false)
{
Console.WriteLine($"緊急: {urgent}, 内容: {message}");
}
このメソッドを別のプロジェクトから次のように呼び出していたとします。
C#Send("確認してください");
後からデフォルト値を変更したとします。
C#public void Send(string message, bool urgent = true)
{
Console.WriteLine($"緊急: {urgent}, 内容: {message}");
}
このとき、呼び出し側を再コンパイルしていない場合、古いデフォルト値のfalseが使われ続けることがあります。
オプション引数のデフォルト値は呼び出し側に埋め込まれるため、publicなメソッドやライブラリでデフォルト値を変更する場合は、影響範囲をよく確認する必要があります。
4. 名前付き引数と組み合わせた使い方
4-1. 名前付き引数とは?
名前付き引数とは、メソッドを呼び出すときに、引数名を指定して値を渡す書き方です。
通常の呼び出しでは、引数の順番に従って値を渡します。
C#void CreateUser(string name, int age, bool isAdmin = false)
{
Console.WriteLine($"名前: {name}");
Console.WriteLine($"年齢: {age}");
Console.WriteLine($"管理者: {isAdmin}");
}
CreateUser("田中", 25, true);
この場合、"田中"はname、25はage、trueはisAdminに渡されます。
名前付き引数を使うと、次のように引数名を明示できます。
C#CreateUser(name: "田中", age: 25, isAdmin: true);
どの値がどの引数に対応しているのかがわかりやすくなります。
4-2. オプション引数を名前付きで指定する例
オプション引数は、名前付き引数と組み合わせるとさらに便利です。
C#void PrintReport(string title, bool showDate = true, bool showPageNumber = true)
{
Console.WriteLine($"タイトル: {title}");
Console.WriteLine($"日付表示: {showDate}");
Console.WriteLine($"ページ番号表示: {showPageNumber}");
}
たとえば、showDateはデフォルトのままで、showPageNumberだけをfalseにしたい場合があります。
通常の順番指定では、次のように書く必要があります。
C#PrintReport("売上レポート", true, false);
しかし、これだけを見ると、trueやfalseが何を意味しているのか少しわかりにくいです。
名前付き引数を使うと、次のように書けます。
C#PrintReport("売上レポート", showPageNumber: false);
showDateは省略されてデフォルト値のtrueが使われ、showPageNumberにはfalseが指定されます。
4-3. 複数のオプション引数を一部だけ指定する方法
複数のオプション引数がある場合、名前付き引数を使うことで、必要な引数だけを指定できます。
C#void SearchProducts(
string keyword,
int page = 1,
int pageSize = 20,
bool onlyInStock = false
)
{
Console.WriteLine($"キーワード: {keyword}");
Console.WriteLine($"ページ: {page}");
Console.WriteLine($"件数: {pageSize}");
Console.WriteLine($"在庫ありのみ: {onlyInStock}");
}
keywordだけ指定する場合です。
C#SearchProducts("パソコン");
pageSizeだけ変更したい場合は、名前付き引数を使うとわかりやすく書けます。
C#SearchProducts("パソコン", pageSize: 50);
onlyInStockだけをtrueにしたい場合も、次のように書けます。
C#SearchProducts("パソコン", onlyInStock: true);
複数指定することもできます。
C#SearchProducts("パソコン", page: 2, onlyInStock: true);
このように、名前付き引数を使うと、途中のオプション引数を省略しながら、必要な値だけ指定できます。
4-4. 名前付き引数を使うメリット
名前付き引数を使うメリットは、コードの意味がわかりやすくなることです。
たとえば、次のコードを見てみましょう。
C#SearchProducts("パソコン", 2, 50, true);
この呼び出しだけを見ると、2や50やtrueが何を表しているのか、メソッド定義を見ないとわかりにくいです。
一方、名前付き引数を使うと次のようになります。
C#SearchProducts("パソコン", page: 2, pageSize: 50, onlyInStock: true);
このように書けば、呼び出し側のコードを読んだだけで意味がわかります。
特にbool型の引数は、trueやfalseだけでは意味が伝わりにくいため、名前付き引数と相性がよいです。
5. オプション引数を使った実践的なコード例
5-1. ログ出力メソッドで使う例
ログ出力メソッドでは、ログレベルや日時表示の有無などにオプション引数を使うと便利です。
C#void WriteLog(string message, string level = "Info", bool showTime = true)
{
string timeText = showTime ? $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] " : "";
Console.WriteLine($"{timeText}[{level}] {message}");
}
通常のログ出力では、メッセージだけを指定します。
C#WriteLog("処理を開始しました");
エラーログとして出力したい場合は、ログレベルを指定します。
C#WriteLog("ファイルの読み込みに失敗しました", level: "Error");
時刻を表示したくない場合は、showTimeをfalseにします。
C#WriteLog("テストログです", showTime: false);
このように、標準的な動作をデフォルト値として用意しておき、必要なときだけ変更できるようにすると使いやすいメソッドになります。
5-2. 検索条件を指定するメソッドで使う例
検索処理では、ページ番号、取得件数、並び順などをオプション引数にすることがあります。
C#void SearchItems(
string keyword,
int page = 1,
int pageSize = 20,
string sortOrder = "newest"
)
{
Console.WriteLine($"検索キーワード: {keyword}");
Console.WriteLine($"ページ番号: {page}");
Console.WriteLine($"表示件数: {pageSize}");
Console.WriteLine($"並び順: {sortOrder}");
}
基本的な検索では、キーワードだけを指定します。
C#SearchItems("C#");
2ページ目を表示したい場合は、pageを指定します。
C#SearchItems("C#", page: 2);
表示件数を増やしたい場合は、pageSizeを指定します。
C#SearchItems("C#", pageSize: 50);
古い順に並べたい場合は、sortOrderを指定します。
C#SearchItems("C#", sortOrder: "oldest");
検索条件は省略されることも多いため、オプション引数と相性がよい処理です。
5-3. メッセージ表示メソッドで使う例
画面やコンソールにメッセージを表示する処理でも、オプション引数はよく使えます。
C#void ShowMessage(string message, string title = "お知らせ", bool isImportant = false)
{
Console.WriteLine($"[{title}]");
if (isImportant)
{
Console.WriteLine("重要なメッセージです。");
}
Console.WriteLine(message);
}
通常のメッセージ表示です。
C#ShowMessage("保存が完了しました。");
タイトルを指定する場合です。
C#ShowMessage("ログインに失敗しました。", title: "エラー");
重要なメッセージとして表示する場合です。
C#ShowMessage("パスワードを変更してください。", isImportant: true);
このように、表示方法を少しだけ変えたい場合に、オプション引数を使うと便利です。
5-4. コンストラクタでオプション引数を使う例
オプション引数は、メソッドだけでなくコンストラクタでも使えます。
C#class User
{
public string Name { get; }
public int Age { get; }
public bool IsAdmin { get; }
public User(string name, int age = 0, bool isAdmin = false)
{
Name = name;
Age = age;
IsAdmin = isAdmin;
}
}
このクラスは、nameだけを必須にし、ageとisAdminは省略できるようにしています。
C#User user1 = new User("田中");
User user2 = new User("佐藤", 30);
User user3 = new User("鈴木", 25, true);
名前付き引数を使うと、さらに読みやすくなります。
C#User admin = new User("山田", isAdmin: true);
このように、コンストラクタでも初期値を設定したい項目にオプション引数を使えます。
ただし、コンストラクタの引数が多くなりすぎる場合は、オプション引数ではなく、設定用のクラスやビルダーパターンを検討したほうがよい場合もあります。
6. オプション引数を使うメリット
6-1. メソッド呼び出しを簡潔に書ける
オプション引数の大きなメリットは、メソッド呼び出しを簡潔に書けることです。
たとえば、次のようなメソッドがあるとします。
C#void SendMail(string to, string subject, string body, bool isHtml = false)
{
Console.WriteLine($"宛先: {to}");
Console.WriteLine($"件名: {subject}");
Console.WriteLine($"HTML形式: {isHtml}");
Console.WriteLine(body);
}
通常のメール送信では、HTML形式でない場合が多いとします。その場合、isHtmlを省略できます。
C#SendMail("user@example.com", "お知らせ", "本文です");
HTML形式で送信したい場合だけ、値を指定します。
C#SendMail("user@example.com", "お知らせ", "<p>本文です</p>", isHtml: true);
毎回同じ値を指定しなくてよいため、コードがすっきりします。
6-2. 引数のパターン違いに対応しやすい
オプション引数を使うと、呼び出し時の引数パターンに柔軟に対応できます。
C#void CreateButton(string text, int width = 100, int height = 40, bool enabled = true)
{
Console.WriteLine($"テキスト: {text}");
Console.WriteLine($"幅: {width}");
Console.WriteLine($"高さ: {height}");
Console.WriteLine($"有効: {enabled}");
}
標準サイズのボタンを作る場合です。
C#CreateButton("保存");
幅だけを変更する場合です。
C#CreateButton("保存", width: 150);
無効状態のボタンを作る場合です。
C#CreateButton("削除", enabled: false);
このように、必要な部分だけを変更できるため、さまざまなパターンに対応しやすくなります。
6-3. オーバーロードの数を減らせる
オプション引数を使うと、メソッドのオーバーロードの数を減らせます。
オプション引数を使わない場合、次のように複数のメソッドを用意することがあります。
C#void Print(string message)
{
Print(message, true);
}
void Print(string message, bool newLine)
{
if (newLine)
{
Console.WriteLine(message);
}
else
{
Console.Write(message);
}
}
オプション引数を使うと、次のように1つのメソッドで表現できます。
C#void Print(string message, bool newLine = true)
{
if (newLine)
{
Console.WriteLine(message);
}
else
{
Console.Write(message);
}
}
このように、単純な引数違いであれば、オプション引数を使うことでコード量を減らせます。
6-4. 初心者でも読みやすいコードにしやすい
オプション引数は、適切に使えば初心者にも読みやすいコードになります。
特に、名前付き引数と組み合わせると効果的です。
C#CreateButton("削除", enabled: false);
このコードは、「削除ボタンを無効状態で作っている」と直感的に理解できます。
一方、次のように書くと少しわかりにくくなります。
C#CreateButton("削除", 100, 40, false);
falseが何を意味しているのか、メソッド定義を見ないと判断しにくいです。
オプション引数を使うときは、名前付き引数も活用すると、読みやすいコードにしやすくなります。
7. オプション引数の注意点
7-1. オプション引数は必須引数の後ろに書く
C#では、オプション引数は基本的に必須引数の後ろに書く必要があります。
正しい例です。
C#void Sample(string name, int age = 0)
{
Console.WriteLine($"{name}: {age}");
}
nameは必須引数、ageはオプション引数です。
一方、次のようにオプション引数の後ろに必須引数を書くとエラーになります。
C#// エラー
void Sample(int age = 0, string name)
{
Console.WriteLine($"{name}: {age}");
}
なぜなら、呼び出し時にどの値がどの引数に対応するのか、わかりにくくなるためです。
C#Sample(20);
このような呼び出しがあったとき、20がageなのか、それとも必須引数が足りないのか判断しづらくなります。
そのため、オプション引数は必須引数の後ろにまとめて書くのが基本です。
7-2. デフォルト値はコンパイル時に埋め込まれる
C#のオプション引数で特に重要なのが、デフォルト値は呼び出し側にコンパイル時に埋め込まれるという点です。
たとえば、次のようなメソッドがあるとします。
C#public void ShowStatus(string status = "New")
{
Console.WriteLine(status);
}
呼び出し側で次のように書いた場合です。
C#ShowStatus();
この呼び出しは、コンパイル時に"New"というデフォルト値を使う形で処理されます。
その後、メソッド側のデフォルト値を次のように変更したとします。
C#public void ShowStatus(string status = "Updated")
{
Console.WriteLine(status);
}
しかし、呼び出し側を再コンパイルしていなければ、古いデフォルト値の"New"が使われることがあります。
この仕組みを知らないと、「デフォルト値を変更したのに反映されない」というトラブルにつながります。
7-3. publicなAPIでデフォルト値を変更すると影響が出る
publicなメソッドや外部に公開するライブラリでオプション引数を使う場合は、デフォルト値の変更に注意が必要です。
次のようなpublicメソッドがあるとします。
C#public void Export(string fileName, string format = "csv")
{
Console.WriteLine($"{fileName} を {format} 形式で出力します");
}
後からデフォルト値を"json"に変更したとします。
C#public void Export(string fileName, string format = "json")
{
Console.WriteLine($"{fileName} を {format} 形式で出力します");
}
この変更は、一見すると小さな変更に見えます。しかし、このメソッドを利用している側が再コンパイルされていない場合、古い"csv"が使われ続ける可能性があります。
publicなAPIでは、デフォルト値の変更も互換性に影響する変更として扱うべきです。
不安な場合は、オプション引数ではなく、オーバーロードを用意する方法も検討しましょう。
7-4. ref・out・paramsとの組み合わせに注意する
C#では、refやoutの引数にデフォルト値を指定することはできません。
C#// エラー
void UpdateValue(ref int value = 0)
{
}
refやoutは、呼び出し側の変数を直接受け渡す仕組みです。そのため、単純なデフォルト値を設定するオプション引数とは性質が合いません。
また、paramsは可変長引数を表すキーワードで、引数リストの最後に書く必要があります。
C#void PrintAll(params string[] messages)
{
foreach (string message in messages)
{
Console.WriteLine(message);
}
}
params自体が「0個以上の値を渡せる」仕組みなので、オプション引数と似た使い方ができる場合もあります。ただし、paramsとオプション引数を同時に使うと、呼び出し方がわかりにくくなることがあります。
C#void PrintMessages(string title = "メッセージ", params string[] messages)
{
Console.WriteLine(title);
foreach (string message in messages)
{
Console.WriteLine(message);
}
}
このような定義はできますが、呼び出し側で意図がわかりにくくなる場合があります。
C#PrintMessages("こんにちは");
この呼び出しでは、"こんにちは"はtitleに渡されます。messagesに渡したい場合は、呼び出し方をよく考える必要があります。
オプション引数、ref、out、paramsは、それぞれ役割が違います。無理に組み合わせるより、読みやすさを優先して設計しましょう。
7-5. 使いすぎるとメソッドの意図が分かりにくくなる
オプション引数は便利ですが、使いすぎるとメソッドの意図がわかりにくくなります。
たとえば、次のようなメソッドを考えてみます。
C#void CreateReport(
string title,
bool showHeader = true,
bool showFooter = true,
bool includeChart = false,
bool includeSummary = true,
bool sendMail = false
)
{
}
このようにオプション引数が多すぎると、呼び出し側のコードもわかりにくくなります。
C#CreateReport("月次レポート", true, false, true, true, false);
このコードだけを見ても、それぞれのtrueやfalseが何を表しているのか判断しづらいです。
名前付き引数を使えば多少読みやすくなります。
C#CreateReport(
"月次レポート",
showFooter: false,
includeChart: true,
sendMail: false
);
それでも、引数が多すぎる場合は、メソッドの責務が大きくなっている可能性があります。
そのような場合は、設定用のクラスを作ることも検討しましょう。
C#class ReportOptions
{
public bool ShowHeader { get; set; } = true;
public bool ShowFooter { get; set; } = true;
public bool IncludeChart { get; set; } = false;
public bool IncludeSummary { get; set; } = true;
public bool SendMail { get; set; } = false;
}
オプション引数は、少数の補助的な設定に使うと効果的です。
8. オプション引数とメソッドのオーバーロードの違い
8-1. メソッドのオーバーロードとは?
メソッドのオーバーロードとは、同じ名前のメソッドを、引数の数や型を変えて複数定義することです。
C#void Print(string message)
{
Console.WriteLine(message);
}
void Print(string message, int count)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(message);
}
}
このように、同じPrintという名前でも、引数の数が違うため別のメソッドとして定義できます。
呼び出し側では、渡した引数に応じて呼び出されるメソッドが決まります。
C#Print("こんにちは");
Print("こんにちは", 3);
オーバーロードは、引数のパターンごとに処理を分けたい場合に便利です。
8-2. オプション引数との使い分け
オプション引数とオーバーロードは、どちらも「引数の違いに対応する」ために使えます。
オプション引数を使う例です。
C#void Print(string message, int count = 1)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(message);
}
}
オーバーロードを使う例です。
C#void Print(string message)
{
Print(message, 1);
}
void Print(string message, int count)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(message);
}
}
どちらも似たような呼び出しができます。
C#Print("こんにちは");
Print("こんにちは", 3);
単純に「指定されなければ標準値を使う」だけであれば、オプション引数が便利です。
一方で、引数の型や数によって処理内容を明確に分けたい場合は、オーバーロードが向いています。
8-3. オーバーロードを使ったほうがよいケース
次のような場合は、オプション引数よりオーバーロードを使ったほうがよいことがあります。
まず、引数の型が大きく異なる場合です。
C#void Load(int id)
{
Console.WriteLine($"IDで読み込み: {id}");
}
void Load(string fileName)
{
Console.WriteLine($"ファイル名で読み込み: {fileName}");
}
このように、IDで読み込む場合とファイル名で読み込む場合では意味が異なります。この場合は、オーバーロードで分けたほうが自然です。
また、処理内容が大きく変わる場合もオーバーロードが向いています。
C#void Save(string text)
{
Console.WriteLine("テキストとして保存");
}
void Save(byte[] data)
{
Console.WriteLine("バイナリとして保存");
}
引数の種類によって処理が変わる場合、オプション引数で無理にまとめるより、オーバーロードで明確に分けたほうが読みやすくなります。
さらに、publicなAPIで将来的な互換性を重視する場合も、オーバーロードが選ばれることがあります。オプション引数のデフォルト値は呼び出し側に埋め込まれるため、変更時に注意が必要だからです。
8-4. オプション引数を使ったほうがよいケース
次のような場合は、オプション引数が向いています。
まず、標準的な値が明確に決まっている場合です。
C#void Connect(string host, int port = 80)
{
Console.WriteLine($"{host}:{port} に接続します");
}
HTTPの標準ポートとして80を使うような場合、オプション引数にすると自然です。
また、補助的な設定を少しだけ変えたい場合にも向いています。
C#void Notify(string message, bool sound = true)
{
Console.WriteLine(message);
if (sound)
{
Console.WriteLine("通知音を鳴らします");
}
}
通常は通知音を鳴らし、必要なときだけ無音にするようなケースです。
C#Notify("処理が完了しました");
Notify("処理が完了しました", sound: false);
このように、処理の本質は同じで、一部の設定だけを変える場合は、オプション引数を使うとコードが簡潔になります。
9. 初心者がつまずきやすいエラーと対処法
9-1. 必須引数の前にオプション引数を書いてしまう
初心者がよくつまずくのが、オプション引数の後ろに必須引数を書いてしまうケースです。
C#// エラー
void Register(string role = "User", string name)
{
Console.WriteLine($"{name}: {role}");
}
このような書き方はできません。
対処法は、必須引数を先に書き、オプション引数を後ろに書くことです。
C#void Register(string name, string role = "User")
{
Console.WriteLine($"{name}: {role}");
}
呼び出しは次のようになります。
C#Register("田中");
Register("佐藤", "Admin");
オプション引数は、基本的に引数リストの後ろにまとめると覚えておきましょう。
9-2. デフォルト値に変数を指定してしまう
次によくあるのが、デフォルト値に変数を指定してしまうケースです。
C#int defaultPageSize = 20;
// エラー
void Search(string keyword, int pageSize = defaultPageSize)
{
Console.WriteLine($"{keyword}: {pageSize}");
}
オプション引数のデフォルト値には、コンパイル時に確定できる定数を指定する必要があります。
対処法として、定数を使う方法があります。
C#const int DefaultPageSize = 20;
void Search(string keyword, int pageSize = DefaultPageSize)
{
Console.WriteLine($"{keyword}: {pageSize}");
}
constで定義された値はコンパイル時定数なので、オプション引数のデフォルト値として使えます。
ただし、static readonlyは実行時に値が決まるため、デフォルト値には使えません。
また、実行時に決めたい値がある場合は、nullを使ってメソッド内部で設定する方法が便利です。
C#void Search(string keyword, int? pageSize = null)
{
int actualPageSize = pageSize ?? 20;
Console.WriteLine($"{keyword}: {actualPageSize}");
}
9-3. 引数の順番を間違えて呼び出してしまう
複数のオプション引数があると、呼び出し時に順番を間違えることがあります。
C#void CreateUser(string name, int age = 0, bool isAdmin = false)
{
Console.WriteLine($"名前: {name}");
Console.WriteLine($"年齢: {age}");
Console.WriteLine($"管理者: {isAdmin}");
}
次の呼び出しは正しいです。
C#CreateUser("田中", 30, true);
しかし、isAdminだけ指定したい場合に、次のようには書けません。
C#// 意図どおりにならない
CreateUser("田中", true);
第2引数はageなので、boolを渡すと型が合わずエラーになります。
このような場合は、名前付き引数を使います。
C#CreateUser("田中", isAdmin: true);
複数のオプション引数がある場合は、名前付き引数を使うことで、順番によるミスを防ぎやすくなります。
9-4. デフォルト値の変更が反映されない
オプション引数では、デフォルト値の変更が呼び出し側にすぐ反映されないことがあります。
たとえば、ライブラリ側のメソッドが次のようになっていたとします。
C#public void Display(string message = "Hello")
{
Console.WriteLine(message);
}
呼び出し側では次のように使っています。
C#Display();
その後、ライブラリ側のデフォルト値を変更します。
C#public void Display(string message = "こんにちは")
{
Console.WriteLine(message);
}
このとき、呼び出し側を再コンパイルしていないと、古い"Hello"が使われる場合があります。
対処法は、呼び出し側も再コンパイルすることです。
また、外部に公開するメソッドでは、デフォルト値を頻繁に変更しない設計にすることも大切です。将来的に変わる可能性がある値は、オプション引数ではなく、メソッド内部で決定する仕組みにしたほうが安全な場合があります。
10. C#のオプション引数に関するよくある質問
10-1. オプション引数はいつ使うべき?
オプション引数は、「指定しなくても自然な標準値がある引数」に使うのがおすすめです。
たとえば、次のようなケースです。
C#void Download(string url, int timeoutSeconds = 30)
{
Console.WriteLine($"{url} を {timeoutSeconds}秒のタイムアウトでダウンロードします");
}
通常は30秒のタイムアウトでよいけれど、必要なときだけ変更したい場合に向いています。
C#Download("https://example.com");
Download("https://example.com", timeoutSeconds: 60);
逆に、値を省略すると意味があいまいになる引数には使わないほうがよいです。
C#void RegisterUser(string name = "ゲスト")
{
}
ユーザー登録で名前を省略できる設計が本当に適切かどうかは、よく考える必要があります。
オプション引数は、便利だから使うのではなく、「省略しても意味が自然か」を基準に使いましょう。
10-2. オプション引数とnull許容型はどう使い分ける?
オプション引数とnull許容型は、組み合わせて使うこともあります。
たとえば、現在日時をデフォルトにしたい場合、DateTime.Nowをオプション引数のデフォルト値にすることはできません。
C#// エラー
void PrintDate(DateTime date = DateTime.Now)
{
}
このような場合は、null許容型を使います。
C#void PrintDate(DateTime? date = null)
{
DateTime actualDate = date ?? DateTime.Now;
Console.WriteLine(actualDate);
}
引数が省略された場合はnullになり、メソッド内部でDateTime.Nowを設定します。
C#PrintDate();
特定の日付を指定したい場合は、値を渡します。
C#PrintDate(new DateTime(2026, 1, 1));
つまり、コンパイル時に決まる値を標準値にしたい場合は通常のオプション引数、実行時に決めたい値を使いたい場合はnullをデフォルトにしてメソッド内で処理する、という使い分けができます。
10-3. 複数のオプション引数を設定してもよい?
複数のオプション引数を設定することはできます。
C#void CreateFile(string name, string extension = ".txt", bool overwrite = false)
{
Console.WriteLine($"{name}{extension}");
Console.WriteLine($"上書き: {overwrite}");
}
呼び出し例です。
C#CreateFile("memo");
CreateFile("memo", ".csv");
CreateFile("memo", overwrite: true);
ただし、オプション引数が多すぎると、メソッドの意味がわかりにくくなります。
特にboolのオプション引数が複数ある場合は注意が必要です。
C#CreateFile("memo", ".csv", true);
このtrueが何を表すのか、メソッド定義を見ないとわかりません。
そのため、複数のオプション引数を使う場合は、名前付き引数を使うことをおすすめします。
C#CreateFile("memo", extension: ".csv", overwrite: true);
また、オプション引数が増えすぎた場合は、設定用のクラスを使う設計も検討しましょう。
10-4. オプション引数はインターフェースでも使える?
C#では、インターフェースのメソッドにもオプション引数を定義できます。
C#interface IMessageService
{
void Send(string message, bool urgent = false);
}
実装クラスは次のように書けます。
C#class MessageService : IMessageService
{
public void Send(string message, bool urgent = false)
{
Console.WriteLine($"緊急: {urgent}");
Console.WriteLine(message);
}
}
呼び出し例です。
C#IMessageService service = new MessageService();
service.Send("こんにちは");
この場合、インターフェース側で定義されたデフォルト値が使われます。
ただし、インターフェースと実装クラスで異なるデフォルト値を指定すると、変数の型によって使われるデフォルト値が変わる可能性があり、混乱の原因になります。
C#interface IMessageService
{
void Send(string message, bool urgent = false);
}
class MessageService : IMessageService
{
public void Send(string message, bool urgent = true)
{
Console.WriteLine(urgent);
}
}
このような設計は避けたほうが安全です。
インターフェースでオプション引数を使う場合は、デフォルト値の管理に注意しましょう。特に、公開APIではオプション引数よりもオーバーロードを使ったほうがわかりやすい場合もあります。
まとめ
C#のオプション引数は、メソッドやコンストラクタの引数にデフォルト値を設定し、呼び出し時にその引数を省略できるようにする機能です。
基本的な書き方は、引数の後ろに= デフォルト値を指定するだけです。
C#void Greet(string name = "ゲスト")
{
Console.WriteLine($"こんにちは、{name}さん");
}
このように定義すると、引数を省略して呼び出せます。
C#Greet();
オプション引数を使うと、メソッド呼び出しを簡潔に書ける、引数のパターン違いに対応しやすい、オーバーロードの数を減らせるといったメリットがあります。
一方で、注意点もあります。オプション引数は必須引数の後ろに書く必要があり、デフォルト値にはコンパイル時に確定できる値を指定する必要があります。また、デフォルト値は呼び出し側にコンパイル時に埋め込まれるため、publicなAPIでデフォルト値を変更する場合は注意が必要です。
複数のオプション引数を使う場合は、名前付き引数を組み合わせると読みやすくなります。
C#SearchProducts("パソコン", pageSize: 50, onlyInStock: true);
オプション引数は便利な機能ですが、使いすぎるとメソッドの意図がわかりにくくなることもあります。標準値が明確で、省略しても自然な引数に対して使うのがポイントです。
C#で読みやすく使いやすいメソッドを作るために、オプション引数の仕組みと注意点を正しく理解して活用しましょう。

