C#のoptionalとは?オプション引数・Optional型・null回避の使い分けを初心者向けに解説

はじめに

「C# optional」と検索すると、主に3つの話題が出てきます。1つ目は、メソッドの引数を省略できる「オプション引数」。2つ目は、値があるかないかを表す「Optional型」のような考え方。3つ目は、nullを安全に扱うための「Nullable」やnull回避構文です。

初心者が混乱しやすいのは、C#における「optional」が1つの機能名だけを指しているわけではない点です。C#には省略可能な引数を表す仕組みがありますが、JavaのOptionalやSwiftのOptionalのような、標準でよく使うOptional<T>型が中心にあるわけではありません。

この記事では、C#のoptionalという言葉を「オプション引数」「Optional型的な設計」「null回避」の3つに分けて、初心者にも分かりやすく解説します。

1. C#のoptionalとは?まず押さえたい3つの意味

C#で「optional」と言う場合、文脈によって意味が変わります。英単語としては「任意の」「省略可能な」「あってもなくてもよい」という意味ですが、プログラミングではそれが引数、戻り値、null、安全な設計などに結びつきます。

1-1. 「optional」はC#の正式な型名ではなく文脈で意味が変わる

C#には「optional」というキーワードや、標準で頻繁に使うOptional<T>という基本型があるわけではありません。

一方で、C#には「省略可能な引数」を作るオプション引数があります。また、int?string?のように、値が存在しない可能性を型で表すNullableの仕組みもあります。さらに、ライブラリや独自実装によってOption<T>Optional<T>のような型を使うこともあります。

つまり、C# optionalを理解するには、まず「何がoptionalなのか」を見極めることが大切です。

1-2. C#でoptionalと呼ばれやすいもの一覧

C#でoptionalと呼ばれやすいものは、主に次のようなものです。

呼ばれ方主な意味代表的な書き方
オプション引数引数を省略できるvoid Method(int count = 10)
Nullable値型値型にnullを許可するint? age
Nullable参照型参照型にnullの可能性を明示するstring? name
null回避構文nullによる例外を防ぐuser?.Name ?? "未設定"
Optional型風の設計値の有無を型で表すOptional<T> / Option<T>

C#の省略可能な引数では、パラメーター定義にデフォルト値を指定し、呼び出し時にその引数を省略できます。名前付き引数と組み合わせることで、引数の意味を明確にしながら呼び出せます。

1-3. 初心者が混同しやすい「オプション引数」「Optional型」「null回避」の違い

オプション引数は「メソッドを呼び出すときに、引数を省略できるようにする仕組み」です。

Optional型は「値があるかないかを、型として表現する考え方」です。C#標準ではNullableや独自クラス、外部ライブラリで近い考え方を実現することが多いです。

null回避は「nullが原因でプログラムが落ちることを防ぐための書き方」です。?.??、Nullable参照型などを使います。

まとめると、オプション引数は「入力の省略」、Optional型的な設計は「値の有無の表現」、null回避は「安全な処理」のために使います。

2. C#のオプション引数とは

C#のオプション引数とは、メソッド、コンストラクター、インデクサーなどで、引数を省略して呼び出せるようにする機能です。

例えば、検索処理で「最大件数を指定しなければ10件にする」「ログ出力でレベルを指定しなければInfoにする」といった場面で使えます。

2-1. オプション引数の基本構文

基本構文は、パラメーターにデフォルト値を指定するだけです。

C#
void Greet(string name = "ゲスト")
{
Console.WriteLine($"こんにちは、{name}さん");
}

このメソッドは、次のように呼び出せます。

C#
Greet("田中");
Greet();

Greet("田中")ではname"田中"が入ります。Greet()では引数を省略しているため、デフォルト値の"ゲスト"が使われます。

2-2. デフォルト値を指定する書き方

オプション引数では、パラメーター名の後ろに= デフォルト値を書きます。

C#
void PrintMessage(string message, int count = 1)
{
for (int i = 0; i < count; i++)
{
Console.WriteLine(message);
}
}

呼び出し例は次のとおりです。

C#
PrintMessage("Hello");
PrintMessage("Hello", 3);

1つ目はcountを省略しているため、1回だけ表示されます。2つ目はcount3を指定しているため、3回表示されます。

省略可能な引数のデフォルト値には制限があり、定数式、値型に対するnew ValType()、値型に対するdefault(ValType)などが使えます。

2-3. 名前付き引数と組み合わせる方法

オプション引数は、名前付き引数と組み合わせると読みやすくなります。

C#
void CreateUser(string name, int age = 20, bool isAdmin = false)
{
Console.WriteLine($"{name}, {age}, Admin: {isAdmin}");
}

通常の呼び出しは次のようになります。

C#
CreateUser("佐藤", 30, true);

しかし、これだけでは30trueが何を意味するのか分かりにくい場合があります。名前付き引数を使うと、次のように書けます。

C#
CreateUser("佐藤", isAdmin: true);

この例では、ageはデフォルト値の20が使われ、isAdminだけを明示的に指定しています。名前付き引数は、引数を位置ではなくパラメーター名で指定できる仕組みです。

2-4. オプション引数が使える場面

オプション引数は、次のような場面で便利です。

C#
void Log(string message, string level = "Info")
{
Console.WriteLine($"[{level}] {message}");
}

通常のログなら次のように呼び出します。

C#
Log("処理を開始しました");

エラーとして出力したい場合だけ、レベルを指定します。

C#
Log("ファイルが見つかりません", "Error");

このように、ほとんどの場合は同じ値を使い、必要なときだけ変更したい引数に向いています。

2-5. オプション引数の注意点

オプション引数を使うときは、必須引数の後ろに書く必要があります。

C#
// OK
void Send(string to, string subject = "件名なし")
{
}

次のように、オプション引数の後ろに必須引数を書くことはできません。

C#
// NG
void Send(string subject = "件名なし", string to)
{
}

また、デフォルト値は呼び出し側にも影響します。特にライブラリや公開APIでオプション引数のデフォルト値を変更すると、利用側の再コンパイルが必要になることがあります。そのため、外部に公開するAPIでは慎重に使うべきです。

3. C#にOptional型はある?ない?

「C# optional」と調べる人の中には、「C#にもJavaのOptionalのような型があるのか」を知りたい人も多いでしょう。

結論から言うと、C#標準には、JavaのOptional<T>のように広く使う専用のOptional型は基本的にありません。その代わりに、Nullable、nullチェック、Tryパターン、独自型、外部ライブラリなどを使って「値がないかもしれない」状態を表現します。

3-1. C#標準にはOptional型は基本的に存在しない

C#で標準的に使われるのは、値型にnullを許可するNullable<T>や、参照型のnull可能性を明示するNullable参照型です。

例えば、intは通常nullを持てませんが、int?にするとnullを持てます。

C#
int? age = null;

int?Nullable<int>の短縮形です。Nullable<T>には、値があるかを確認するHasValueや、値を取り出すValueがあります。HasValuefalseの状態でValueにアクセスすると例外が発生します。

3-2. 他言語のOptional型との違い

JavaのOptional<T>やSwiftのOptionalは、「値が存在しない可能性」を型で強く表現するためによく使われます。

C#でも同じ考え方は重要ですが、標準的な書き方としては、値型ならint?、参照型ならstring?、処理の失敗ならbool TryGetValue(...)のようなTryパターンを使うことが多いです。

そのため、C#でOptional型を探すよりも、まずは次の3つを理解する方が実務では役立ちます。

C#
int? value = null;
string? name = null;
bool success = int.TryParse("123", out int number);

3-3. C#でOptional型に近い考え方を実現する方法

C#でOptional型に近い考え方を実現する方法はいくつかあります。

1つ目は、Nullableを使う方法です。

C#
int? FindAge(string name)
{
if (name == "Alice")
{
return 20;
}

return null;
}

2つ目は、Tryパターンを使う方法です。

C#
bool TryFindAge(string name, out int age)
{
if (name == "Alice")
{
age = 20;
return true;
}

age = default;
return false;
}

3つ目は、自分でOptional<T>のような型を作る方法です。

C#
public class Optional<T>
{
public bool HasValue { get; }
public T? Value { get; }

private Optional(T? value, bool hasValue)
{
Value = value;
HasValue = hasValue;
}

public static Optional<T> Some(T value) => new Optional<T>(value, true);
public static Optional<T> None() => new Optional<T>(default, false);
}

3-4. ライブラリでOptional型を使うケース

C#のプロジェクトでは、関数型プログラミング寄りのライブラリを使ってOption<T>Maybe<T>のような型を導入することがあります。

このような型を使うと、「値がない可能性」をnullではなく型で表せます。例えば、戻り値がOptional<User>であれば、呼び出し側は「ユーザーが見つからない可能性がある」と分かります。

ただし、チーム全体がその設計に慣れていない場合、かえってコードが読みにくくなることもあります。初心者のうちは、まずC#標準のNullableやnull回避構文を理解してから、必要に応じてOptional型風の設計を学ぶのがおすすめです。

3-5. Optional型を自作する場合の考え方

Optional型を自作する場合は、単にValueを持つだけでは不十分です。重要なのは、「値がある状態」と「値がない状態」を安全に区別できることです。

最低限、次のような情報を持たせます。

C#
public readonly struct Optional<T>
{
private readonly T? _value;

public bool HasValue { get; }

public T Value
{
get
{
if (!HasValue)
{
throw new InvalidOperationException("値がありません");
}

return _value!;
}
}

private Optional(T value)
{
_value = value;
HasValue = true;
}

public static Optional<T> Some(T value) => new Optional<T>(value);
public static Optional<T> None => new Optional<T>();
}

このように、HasValueで値の有無を確認し、値がない状態で無理に取り出せないようにします。

4. null回避としてのoptionalの考え方

C# optionalを理解するうえで、null回避は非常に重要です。なぜなら、値が「あるかもしれないし、ないかもしれない」というoptionalな状態は、C#ではnullとして表現されることが多いからです。

4-1. C#でnullが問題になりやすい理由

C#では、参照型の変数にnullが入ることがあります。たとえば、次のようなコードです。

C#
string? name = null;
Console.WriteLine(name.Length);

namenullの状態でLengthにアクセスすると、NullReferenceExceptionが発生します。

このようなエラーは、実行するまで気づきにくいことがあります。そのため、C#ではNullable参照型やnull条件演算子、null合体演算子などを使って、nullによる問題を減らします。Nullable参照型は、NullReferenceExceptionが発生する可能性を下げるための機能群です。

4-2. Nullable参照型とは

Nullable参照型とは、参照型に対して「nullを許可するかどうか」をコード上で明示する仕組みです。

C#
string name = "Alice";
string? nickname = null;

stringはnullを想定しない文字列、string?はnullの可能性がある文字列として扱います。

Nullable参照型は、実行時に新しい型を作る仕組みではなく、コンパイラがnullの可能性を警告してくれるための注釈です。

4-3. null許容型とnull非許容型の違い

null許容型とは、nullを入れられる型です。

C#
string? name = null;
int? age = null;

null非許容型とは、基本的にnullを入れない前提の型です。

C#
string name = "Alice";
int age = 20;

値型の場合、intboolには通常nullを入れられません。しかし、int?bool?にすると、値型でもnullを扱えます。Nullable<T>は、値型に値またはnullを持たせるための仕組みです。

4-4. nullチェックの基本パターン

もっとも基本的なnullチェックは、if文を使う方法です。

C#
string? name = GetName();

if (name != null)
{
Console.WriteLine(name.Length);
}
else
{
Console.WriteLine("名前がありません");
}

C#ではパターンマッチングを使って、次のようにも書けます。

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

Nullable値型の場合は、HasValueを使うこともできます。

C#
int? age = GetAge();

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

ただし、Valueを使う前にHasValueを確認しないと、値がない場合に例外が発生するため注意が必要です。

4-5. null条件演算子・null合体演算子の使い方

C#では、null回避のために便利な演算子が用意されています。

null条件演算子?.は、左側がnullでなければメンバーにアクセスし、nullならそのままnullを返します。

C#
string? name = user?.Name;

usernullでなければuser.Nameを取得し、usernullならnamenullになります。C#のメンバーアクセス演算子には、null条件メンバーアクセス?.やnull条件インデクサーアクセス?[]があります。

null合体演算子??は、左側がnullでなければ左側を返し、nullなら右側を返します。

C#
string displayName = user?.Name ?? "ゲスト";

このコードでは、user?.Namenullなら"ゲスト"が使われます。????=はC#のnull合体演算子で、左側がnullでない場合は左側の値を返し、nullの場合は右側の値を使います。

5. オプション引数・Optional型・null回避の使い分け

C# optionalで大切なのは、「何を表現したいのか」によって使い分けることです。引数を省略したいのか、値がない可能性を表したいのか、nullによる例外を避けたいのかで選ぶべき方法が変わります。

5-1. 引数を省略したいならオプション引数

メソッドを呼び出すときに、ある引数を省略したいだけなら、オプション引数を使います。

C#
void Search(string keyword, int page = 1)
{
Console.WriteLine($"{keyword}の{page}ページ目を検索します");
}

呼び出し側は、必要に応じてpageを指定できます。

C#
Search("C# optional");
Search("C# optional", 2);

このように、デフォルト値が自然に決まる場合はオプション引数が向いています。

5-2. 値が存在しない可能性を表現したいならNullable

「値がないかもしれない」ことを表したいなら、Nullableを使います。

C#
int? score = GetScore();

scorenullなら、まだ点数が登録されていないことを表せます。

C#
if (score is null)
{
Console.WriteLine("点数は未登録です");
}
else
{
Console.WriteLine($"点数は{score}点です");
}

値型ならint?、参照型ならstring?のように、nullの可能性を型で示すことができます。

5-3. nullを避けて安全に扱いたいならOptional型的な設計

nullを多用すると、どこで値がないのか分かりにくくなることがあります。その場合は、Optional型的な設計が役立ちます。

C#
Optional<User> FindUser(int id)
{
User? user = FindUserOrNull(id);

if (user is null)
{
return Optional<User>.None;
}

return Optional<User>.Some(user);
}

このようにすると、戻り値を見ただけで「ユーザーが見つからない可能性がある」と分かります。

ただし、C#の一般的なコードでは、まずUser?TryGetUserのような書き方を検討することが多いです。Optional型風の設計は、プロジェクト全体の方針として採用するのがよいでしょう。

5-4. 戻り値で「値なし」を表現する場合の選び方

戻り値で「値なし」を表す場合は、状況に応じて選びます。

状況おすすめ
値型で未設定を表したいint?などのNullable
参照型で見つからない可能性があるUser?
成功・失敗を明確に分けたいTryXxxパターン
nullを設計上避けたいOptional<T>Result<T>
エラー理由も返したいResult<T>や例外

例えば、文字列を数値に変換する処理では、C#でよく使われるTryパターンが分かりやすいです。

C#
if (int.TryParse("123", out int number))
{
Console.WriteLine(number);
}
else
{
Console.WriteLine("変換できませんでした");
}

5-5. 実務で迷ったときの判断基準

実務で迷ったときは、次の順番で考えると分かりやすくなります。

まず、単に引数を省略したいだけならオプション引数を使います。

次に、値がない可能性を表したいならNullableを使います。

そして、nullを使うと意図が分かりにくい場合や、値なし状態を明示的に扱いたい場合はOptional型的な設計を検討します。

初心者のうちは、いきなりOptional型を自作するよりも、??.??TryParseif (x is null)をしっかり理解することが重要です。

6. C# optionalの具体的なコード例

ここからは、C# optionalに関係する具体的なコード例を見ていきます。

6-1. オプション引数を使ったメソッド例

C#
void ShowProfile(string name, int age = 18)
{
Console.WriteLine($"名前: {name}");
Console.WriteLine($"年齢: {age}");
}

呼び出し例です。

C#
ShowProfile("Alice");
ShowProfile("Bob", 25);

ShowProfile("Alice")では、ageを省略しているため18が使われます。ShowProfile("Bob", 25)では、指定した25が使われます。

6-2. 名前付き引数で読みやすくする例

引数が複数ある場合、名前付き引数を使うと読みやすくなります。

C#
void SendEmail(string to, string subject = "件名なし", bool urgent = false)
{
Console.WriteLine($"To: {to}");
Console.WriteLine($"Subject: {subject}");
Console.WriteLine($"Urgent: {urgent}");
}

呼び出し例です。

C#
SendEmail("test@example.com", urgent: true);

この書き方なら、subjectはデフォルト値の"件名なし"を使い、urgentだけをtrueにできます。

6-3. Nullableを使った値なし表現の例

値がない可能性がある場合は、Nullableを使います。

C#
int? GetDiscountRate(bool isMember)
{
if (isMember)
{
return 10;
}

return null;
}

呼び出し側では、nullチェックをします。

C#
int? discountRate = GetDiscountRate(false);

if (discountRate is null)
{
Console.WriteLine("割引はありません");
}
else
{
Console.WriteLine($"{discountRate}%割引です");
}

nullを「割引なし」という意味で使っています。ただし、業務上の意味が複雑な場合は、0null、専用型のどれが適切かをよく考える必要があります。

6-4. null合体演算子でデフォルト値を返す例

??を使うと、nullの場合のデフォルト値を簡潔に書けます。

C#
string? inputName = null;
string name = inputName ?? "ゲスト";

Console.WriteLine(name);

inputNamenullなので、nameには"ゲスト"が入ります。

メソッドでもよく使います。

C#
string GetDisplayName(string? name)
{
return name ?? "未設定";
}

null条件演算子と組み合わせると、さらに安全に書けます。

C#
string displayName = user?.Profile?.Name ?? "ゲスト";

userProfilenullでも、例外を避けながら"ゲスト"を返せます。

6-5. Optional型風のクラスを使う例

Optional型風のクラスを使うと、値の有無を明示できます。

C#
public class Optional<T>
{
public bool HasValue { get; }
private readonly T? _value;

public T Value
{
get
{
if (!HasValue)
{
throw new InvalidOperationException("値がありません");
}

return _value!;
}
}

private Optional(T? value, bool hasValue)
{
_value = value;
HasValue = hasValue;
}

public static Optional<T> Some(T value)
{
return new Optional<T>(value, true);
}

public static Optional<T> None()
{
return new Optional<T>(default, false);
}
}

使用例です。

C#
Optional<string> FindName(int id)
{
if (id == 1)
{
return Optional<string>.Some("Alice");
}

return Optional<string>.None();
}

呼び出し側では、HasValueを確認します。

C#
var result = FindName(2);

if (result.HasValue)
{
Console.WriteLine(result.Value);
}
else
{
Console.WriteLine("名前が見つかりません");
}

このように、Optional型風の設計では、値がない可能性を明示的に扱えます。

7. C# optionalでよくあるエラーと対処法

C# optionalに関係するコードでは、いくつかのよくあるミスがあります。ここでは初心者がつまずきやすいポイントを紹介します。

7-1. オプション引数の後ろに必須引数を書いてしまう

オプション引数の後ろに必須引数を書くとエラーになります。

C#
// NG
void CreateUser(string role = "User", string name)
{
}

正しくは、必須引数を先に書きます。

C#
// OK
void CreateUser(string name, string role = "User")
{
}

オプション引数は「省略される可能性がある引数」なので、後ろに必須引数があると呼び出し方が曖昧になります。

7-2. デフォルト値に変数や動的な値を指定してしまう

オプション引数のデフォルト値には、実行時に決まる値を直接指定できません。

C#
string defaultName = "ゲスト";

// NG
void Greet(string name = defaultName)
{
}

この場合は、引数をnull許容にして、メソッド内でデフォルト値を設定します。

C#
void Greet(string? name = null)
{
name ??= "ゲスト";
Console.WriteLine(name);
}

DateTime.Nowのような動的な値も、オプション引数のデフォルト値には向きません。

C#
void PrintTime(DateTime? time = null)
{
DateTime actualTime = time ?? DateTime.Now;
Console.WriteLine(actualTime);
}

7-3. nullを想定せずNullReferenceExceptionが発生する

次のようなコードは危険です。

C#
User? user = FindUser();
Console.WriteLine(user.Name);

usernullの場合、NullReferenceExceptionが発生します。

対処法は、nullチェックを行うことです。

C#
User? user = FindUser();

if (user is not null)
{
Console.WriteLine(user.Name);
}
else
{
Console.WriteLine("ユーザーが見つかりません");
}

または、null条件演算子とnull合体演算子を使います。

C#
Console.WriteLine(user?.Name ?? "ユーザーが見つかりません");

7-4. Nullable警告の意味が分からない

Nullable参照型を有効にしていると、次のような警告が出ることがあります。

C#
string? name = GetName();
Console.WriteLine(name.Length);

これは、namenullかもしれないのにLengthへアクセスしているためです。

対処法は、nullチェックを追加することです。

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

または、デフォルト値を使います。

C#
Console.WriteLine((name ?? "").Length);

Nullable参照型はコンパイル時にnullの可能性を警告する機能であり、実行時の動作そのものを変えるものではありません。

7-5. Optional型を使うべき場面でnullを多用してしまう

戻り値でnullを多用すると、呼び出し側が毎回「このnullは何を意味するのか」を考える必要があります。

C#
User? FindUser(int id)
{
return null;
}

この程度なら問題ない場合もありますが、業務ロジックが複雑になると、nullが「未登録」なのか「権限なし」なのか「エラー」なのか分からなくなることがあります。

その場合は、結果を表す専用型を使うと分かりやすくなります。

C#
public class FindUserResult
{
public bool IsSuccess { get; set; }
public User? User { get; set; }
public string? ErrorMessage { get; set; }
}

単なる「値なし」ならNullable、失敗理由まで扱いたいならResult型のような設計を検討しましょう。

8. C# optionalを使うときのベストプラクティス

C# optionalを正しく使うには、便利さだけでなく、読みやすさと保守性を意識する必要があります。

8-1. オプション引数は単純なデフォルト値に限定する

オプション引数は便利ですが、複雑な意味を持たせすぎると分かりにくくなります。

良い例です。

C#
void Log(string message, string level = "Info")
{
}

避けたい例です。

C#
void Process(string mode = "auto")
{
// modeによって複雑に処理が変わる
}

デフォルト値によって大きく処理が変わる場合は、メソッドを分ける、設定オブジェクトを使う、明示的な引数にするなどを検討しましょう。

8-2. API設計では将来の変更に注意する

公開APIやライブラリでオプション引数を使う場合、後からデフォルト値を変えると利用者に影響する可能性があります。

例えば、次のようなAPIがあるとします。

C#
public void Search(string keyword, int limit = 10)
{
}

後からlimit = 20に変更した場合、利用側のビルド状況によって期待通りに反映されないことがあります。公開APIでは、オプション引数よりもオーバーロードや設定オブジェクトの方が安全な場合もあります。

C#
public void Search(string keyword)
{
Search(keyword, 10);
}

public void Search(string keyword, int limit)
{
}

8-3. null許容の意図を型で明確にする

値がnullになる可能性があるなら、型に?を付けて意図を明確にしましょう。

C#
string? nickname;

逆に、nullを許可しないなら?を付けないようにします。

C#
string name;

この違いを明確にすることで、呼び出し側も「nullチェックが必要かどうか」を判断しやすくなります。

8-4. 戻り値の「失敗」「未設定」「値なし」を区別する

戻り値でoptionalな状態を表すときは、「失敗」「未設定」「値なし」を混同しないことが大切です。

例えば、次の3つは似ていますが意味が違います。

C#
// 値が存在しない可能性
User? FindUser(int id)

// 成功・失敗を返す
bool TryFindUser(int id, out User user)

// エラー情報も含める
Result<User> GetUser(int id)

単に見つからないだけならUser?で十分な場合があります。成功・失敗を明確にしたいならTryパターン、エラー理由まで扱いたいならResult型のような設計が向いています。

8-5. 初心者はまずNullableとnull回避構文を優先して学ぶ

初心者がC# optionalを学ぶなら、まず次の順番がおすすめです。

  1. オプション引数

  2. int?などのNullable値型

  3. string?などのNullable参照型

  4. ?.??

  5. TryParseなどのTryパターン

  6. Optional型風の設計

いきなりOptional型を自作するよりも、C#で標準的によく使われる書き方を先に覚える方が、実務のコードを読みやすくなります。

9. C# optionalに関するよくある質問

9-1. C#にOptional型は標準でありますか?

JavaのOptional<T>のように、C#で標準的に広く使うOptional<T>型が用意されているわけではありません。

C#では、値型ならNullable<T>、参照型ならNullable参照型、成功・失敗を表すならTryパターンを使うことが多いです。必要に応じて、外部ライブラリのOption<T>や自作のOptional型を使うこともあります。

9-2. オプション引数とNullableは同じですか?

同じではありません。

オプション引数は、メソッド呼び出し時に引数を省略できる仕組みです。

C#
void Greet(string name = "ゲスト")
{
}

Nullableは、値がnullになる可能性を表す仕組みです。

C#
int? age = null;
string? name = null;

オプション引数は「引数の省略」、Nullableは「値なしの可能性」を表します。

9-3. nullを返すのは悪い設計ですか?

必ずしも悪い設計ではありません。

例えば、検索して見つからなかった場合にnullを返すのは自然なことがあります。

C#
User? FindUser(int id)
{
return null;
}

ただし、nullの意味が曖昧になる場合は注意が必要です。見つからない、権限がない、通信エラーが起きた、など複数の意味をnullだけで表すと、呼び出し側が正しく判断できません。

その場合は、TryパターンやResult型のような設計を使う方が安全です。

9-4. Optional型は自作すべきですか?

初心者が最初からOptional型を自作する必要はあまりありません。

まずは、C#標準のNullable、Nullable参照型、null条件演算子、null合体演算子、Tryパターンを理解する方が大切です。

Optional型を自作するのは、プロジェクト全体で「nullを避けて値の有無を明示する」という方針がある場合や、関数型プログラミング寄りの設計を採用する場合に検討するとよいでしょう。

9-5. 初心者はどれから覚えるべきですか?

初心者は、まずオプション引数とNullableから覚えるのがおすすめです。

オプション引数は、メソッドの使い勝手をよくするために役立ちます。

C#
void Print(string message, int count = 1)
{
}

Nullableは、値がない可能性を安全に扱うために必要です。

C#
string? name = null;
int? age = null;

その後、?.??を覚えると、null回避のコードを簡潔に書けるようになります。

まとめ

C# optionalという言葉は、1つの機能だけを指すものではありません。主に「オプション引数」「Optional型的な設計」「null回避」の3つの意味で使われます。

引数を省略したいなら、オプション引数を使います。

C#
void Greet(string name = "ゲスト")
{
}

値がない可能性を表したいなら、Nullableを使います。

C#
int? age = null;
string? name = null;

nullによる例外を避けたいなら、null条件演算子やnull合体演算子を使います。

C#
string displayName = user?.Name ?? "ゲスト";

また、nullをできるだけ避けて値の有無を明示したい場合は、Optional型風の設計や外部ライブラリを検討できます。

初心者はまず、C#の標準的な書き方であるオプション引数、Nullable、?.??を押さえることが大切です。そのうえで、必要に応じてOptional型的な設計を学ぶと、より安全で読みやすいC#コードを書けるようになります。