Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】What's New in C# 14: Null-Conditional Assignments

2025年09月11日に「Dev.to」が公開したITニュース「What's New in C# 14: Null-Conditional Assignments」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

C# 14の新機能「null条件代入」は、`null`チェックなしで安全に値を代入できる。`NullReferenceException`防止とコード簡潔化に役立つ。読み込み専用だった`?.`が代入にも使える。ただし使いすぎはデバッグを難しくする。

ITニュース解説

C#プログラミングにおいて、プログラムの実行中に「NullReferenceException」というエラーに遭遇した経験があるだろうか。これは、存在しない(nullである)オブジェクトのプロパティやメソッドを使おうとしたときに発生する、非常に一般的なエラーである。例えば、設定情報を扱うconfigオブジェクトや、その中のSettingsオブジェクトがnullであるにも関わらず、その中のRetryPolicyというプロパティに値を設定しようとすると、プログラムがクラッシュしてしまう。

これまでのC#では、このようなエラーを防ぐために、値を代入する前にオブジェクトがnullでないかを確認するif文を何重にも書く必要があった。具体的には、if (config?.Settings is not null) のように書いてから、安全にconfig.Settings.RetryPolicy = new ExponentialBackoffRetryPolicy(); といった代入処理を行うのが一般的だった。この?.(Null条件演算子)は、もし左辺がnullであれば、それ以降の処理をスキップしてnullを返すため、NullReferenceExceptionを防ぐのに役立つ。しかし、これは値の読み出しにのみ適用され、代入には直接使えなかったため、結局if文による確認が必要だったのだ。

C# 14では、この問題を根本的に解決する「Null条件代入演算子」が導入された。これは、代入演算子(=)の左辺でも?.が使えるようになるという画期的な変更である。つまり、先の例はC# 14ではたった一行で書けるようになる。

config?.Settings?.RetryPolicy = new ExponentialBackoffRetryPolicy();

この新しい記法では、コンパイラ(C#のコードをコンピュータが実行できる形式に変換するソフトウェア)が、configがnullでないか、そしてconfig.Settingsがnullでないかを順番に確認する。もし途中のいずれかがnullであれば、それ以降のプロパティへの代入処理は完全にスキップされ、NullReferenceExceptionは発生しない。これにより、コードの行数を大幅に削減し、可読性を向上させることができる。

Null条件代入演算子は、オブジェクトのプロパティだけでなく、配列やディクショナリといったインデクサーに対しても適用できる。例えば、customerDataというディクショナリがnullでないことを確認してから値を追加する場合、これまではif (customerData is not null) { customerData["LastLogin"] = DateTime.UtcNow; } と記述していた。C# 14からは、これをcustomerData?["LastLogin"] = DateTime.UtcNow; と簡潔に書くことが可能になる。customerDataがnullであれば、代入は実行されない。

さらに、この機能は通常の代入だけでなく、+=-=のような複合代入演算子とも組み合わせて使用できる。例えば、処理されたアイテム数を記録するresults.ItemsProcessedに値を加算する場合、results?.ItemsProcessed += 5; と書ける。resultsがnullでなければ、ItemsProcessedに5が加算される。

また、既存のNull合体代入演算子(??=)と組み合わせることで、さらに柔軟な処理が可能になる。customer?.Name ??= "Guest"; という記述は、次のように動作する。まず、customerがnullであれば、代入全体がスキップされる。もしcustomerがnullでなければ、次にcustomer.Nameがnullであるかどうかを確認する。もしcustomer.Nameがnullであれば、"Guest"という文字列がcustomer.Nameに代入される。もしcustomer.Nameがnullでなければ、何も変更されない。これにより、オブジェクトが存在する場合に特定のプロパティが初期化されていない場合にのみデフォルト値を設定できる。

この新しいNull条件代入演算子は非常に便利だが、使用する上でいくつかの注意点がある。

一つ目は「副作用の防止」である。Null条件代入演算子では、代入がスキップされる場合、右辺の式は実行されない。例えば、customer?.Id = GenerateNextCustomerId(); というコードがあったとする。もしcustomerがnullであれば、GenerateNextCustomerId()というメソッドは呼び出されない。これは、もしこのメソッドが顧客IDを生成するたびに内部のカウンターをインクリメントするような「副作用」を持つ場合、無駄にIDが消費されることを防ぐ上で理にかなっている。しかし、この挙動を理解しておくことは重要だ。

二つ目は、インクリメント演算子(++)やデクリメント演算子(--)はサポートされない点である。例えば、customer?.TotalOrders++; と書こうとするとエラーになる。C#の設計チームは、これらの演算子をNull条件代入と組み合わせるのが難しく、期待通りの結果にならない可能性があると判断したため、この機能をサポートしないことに決めた。そのため、このような場合は従来通りif文を使うか、+=演算子を利用してcustomer?.TotalOrders += 1;のように記述する必要がある。

三つ目は、過度な使用を避けるべきだという点である。一行で複数のNull条件演算子を連結すると、コードが読みにくくなり、問題が発生した際のデバッグが非常に困難になることがある。例えば、customer?.Orders?.FirstOrDefault()?.OrderNumber = GenerateNewOrderNumber(); のようなコードは一見簡潔に見える。しかし、もしOrderNumberが設定されなかった場合、その原因がcustomerがnullだったのか、customer.Ordersがnullだったのか、Ordersリストが空だったのか、あるいはリストの最初の要素がnullだったのか、といった多くの可能性を検討する必要が出てくる。

このような場合、デバッグを容易にするためには、段階的にnullチェックを行い、それぞれの段階で適切なログを出力する方が良い。

1if (customer is null)
2{
3    // ここで顧客が見つからない旨をログに出力する
4    return;
5}
6
7var firstOrder = customer.Orders?.FirstOrDefault();
8if (firstOrder is null)
9{
10    // ここで注文が見つからない旨をログに出力する
11    return;
12}
13
14firstOrder.OrderNumber = GenerateNewOrderNumber();

このようにすることで、アプリケーションのログを確認するだけで、なぜ注文番号が設定されなかったのかを一目で把握できる。コードの簡潔さだけを追求するのではなく、保守性やデバッグのしやすさも考慮に入れることが、良いシステムエンジニアになるための大切な視点である。

C# 14で導入されるNull条件代入演算子は、従来のC#コードをより簡潔に、そして安全にするための強力なツールである。しかし、その利便性を最大限に活かすためには、その動作原理と制約を理解し、適切な場面で活用することが求められる。この機能は、今後リリースされる.NET 10とともに提供され、C#プログラミングの「品質向上」に大きく貢献するだろう。

関連コンテンツ

関連IT用語