C# 14 の新機能を、従来記法との Before / After で一気に把握できるよう整理しました。
対象は .NET 10 Preview 6 時点の内容で、最新技術レポートです。まずは環境構築、次に早見表、そして各機能の差分とサンプルコードを確認していきます。
動作確認環境
- .NET 10 Preview 6 SDK 10.0.100-preview.6.25358.103
- Visual Studio 2022 Preview 17.14.9-pre.1.0
- MSBuild 17.15.0-preview-25358-103
- Windows 10.0.26100 (x64)
- preview, net10.0
本記事のコードは Preview 仕様に基づく概念サンプルです。今後の Preview/RC で記法が変わる場合があります。(2025/07/15)
目次
- 0. 動かす準備(環境構築)
- 1. C# 14 で何が増えた?早見表
- 2. 各機能の詳細と Before / After 比較
- 3. 互換性と注意点
- 4. 導入手順と段階的適用のコツ
- 5. まとめ
- 6. 参考リンク
0. 動かす準備(環境構築)
<!-- .csproj 抜粋 -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
</Project>
-
.NET 10 Preview SDK をインストール(
dotnet --info
で確認) - Visual Studio 2022 Preview / JetBrains Rider / VS Code + C# Dev Kit など、プレビュー言語を解釈できる IDE を使用
- まずは 小さなプロジェクトで試す → 問題なければ本体へ段階導入
※C# 14 を試すだけなら Community Preview で十分です。
Preview とは?
Preview は正式版(GA/RTM)前に公開される 試用・評価用の先行版 です。C#/.NET では次の特徴があります。
Preview
- 仕様・API が未確定:文法や挙動が変更・撤回される可能性があります
-
明示的オプトインが必要:
<LangVersion>preview</LangVersion>
を指定し、Preview SDK/IDE を導入して使います - 公式サポートは限定的:本番利用は基本自己責任。互換性保証も弱めです
- 次の Preview/RC で壊れることもある:SDK/言語バージョンを固定し、差分を追いましょう
似た用語との違い
用語 | 位置付け | 意味合い |
---|---|---|
Preview | 先行公開段階 | 「試してフィードバックください」段階。壊れる前提 |
RC (Release Candidate) | 最終候補 | 仕様はほぼ確定。致命的問題がなければ正式版へ |
GA / RTM | 正式リリース | 長期サポートや互換性ポリシーが適用される段階 |
どう使い分ける?
- 検証・学習・ブログ執筆:Preview を積極的に試す
- 業務本番コード:RC〜GA 時点で再評価し、正式版に合わせて導入
1. C# 14 で何が増えた?早見表
機能 | 記法/キーワード | 一言メリット |
---|---|---|
Extension members | extension<T>(Type target) { ... } |
メソッドだけでなく プロパティ/インデクサ/静的メンバー まで拡張可 |
field キーワード |
set => field = value; |
バックフィールドを書かずにアクセサ内で参照可 |
Null 条件付き代入 |
obj?.Prop = ... / list?[] = ...
|
左辺に ?. を置けて 安全に代入(右辺評価も抑制) |
nameof(List<>) |
unbound generic 対応 | 型引数なしジェネリック型名を安全に取得 |
Span 暗黙変換拡充 |
Span<T> / ReadOnlySpan<T>
|
パフォーマンスコードでキャスト削減・記述簡素化 |
ラムダのパラメータ修飾子 |
out , ref , in だけ記述可 |
型省略ラムダでも安全性/性能ヒントを表現 |
partial ctor / event | partial |
ソースジェネレータと手書きコードの分担が容易 |
ユーザー定義複合代入 |
operator += など |
独自型の演算子体験を一貫して提供 |
ユーザー定義 ++ / --
|
operator ++ / --
|
数値以外でも自然なインクリメント/デクリメント |
2. 各機能の詳細と Before / After 比較
2-1. Extension members(拡張メンバー)
概要: 拡張メソッドの概念を広げ、プロパティ・インデクサ・静的メンバー まで追加可能に。
使いどころ: 既存型へ軽量 API を自然な形で提供/static
コンテキストでも使いたいユーティリティを型に"ぶら下げる"。
Before(C# 13 まで)
public static class EnumerableExt
{
public static bool IsEmpty<T>(this IEnumerable<T> src) => !src.Any();
// 静的っぽいユーティリティは別メソッドで……
public static IEnumerable<T> Identity<T>() => Enumerable.Empty<T>();
}
After(C# 14 Preview)
public static class EnumerableExt
{
// インスタンス拡張
extension<T>(IEnumerable<T> src)
{
public bool IsEmpty => !src.Any();
public T this[int index] => src.Skip(index).First();
}
// 静的拡張
extension<T>(IEnumerable<T>)
{
public static IEnumerable<T> Identity => Enumerable.Empty<T>();
public static IEnumerable<T> Combine(IEnumerable<T> a, IEnumerable<T> b)
=> a.Concat(b);
}
}
var seq = new[] {1, 2, 3};
bool empty = seq.IsEmpty; // インスタンス拡張
var id = IEnumerable<int>.Identity; // 静的拡張
2-2. field
キーワード(フィールドレス・プロパティ)
概要: バックフィールドを宣言せず、アクセサ内で field
を参照可能。
使いどころ: 単純なバリデーション/ロギングを仕込みたいが、フィールドの宣言は避けたい場合。
Before
private string _message;
public string Message
{
get => _message;
set => _message = value ?? throw new ArgumentNullException(nameof(value));
}
After
public string Message
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(value));
}
2-3. Null 条件付き代入(左辺 ?.
/ ?[]
)
概要: 左辺に ?.
や ?[]
を使い、null なら代入も右辺評価もスキップ。
使いどころ: 「オブジェクトがあれば処理」「なければ何もしない」を一行で書きたいとき。
Before
if (customer != null)
{
customer.Order = CreateOrder();
}
After
customer?.Order = CreateOrder(); // null なら右辺も評価されない
List<string>? log = null;
log?.Add("OK"); // 何も起きない
(log ??= []).Add("init"); // null のときだけ初期化
2-4. nameof(Dictionary<,>)
:unbound generics 対応
概要: 型引数なしジェネリック型を nameof
で扱える。
Before
Console.WriteLine(typeof(Dictionary<,>).Name); // "Dictionary`2" などとなりがち
After
Console.WriteLine(nameof(Dictionary<,>)); // "Dictionary"
2-5. Span 周りの暗黙変換強化
概要: Span<T>
/ ReadOnlySpan<T>
と配列間の変換がより自然に。高パフォーマンスコードの記述が楽になる。
Before
void Process(ReadOnlySpan<byte> data) { /* ... */ }
byte[] bytes = File.ReadAllBytes(path);
Process(bytes.AsSpan()); // 明示的な呼び出しが必要
ReadOnlySpan<byte> ro = bytes.AsSpan();
After
void Process(ReadOnlySpan<byte> data) { /* ... */ }
byte[] bytes = File.ReadAllBytes(path);
Process(bytes); // 暗黙変換で OK
Span<byte> span = bytes;
ReadOnlySpan<byte> ro = span; // これも暗黙変換
2-6. ラムダ式パラメータに修飾子だけ付けられる
概要: 型を書かずに out
, ref
, in
などの修飾子だけ指定できる。
Before
delegate bool TryParseInt(string s, out int result);
TryParseInt parse = (string text, out int result) => int.TryParse(text, out result);
After
delegate bool TryParse<T>(string s, out T result);
TryParse<int> parse = (text, out result) => int.TryParse(text, out result);
2-7. partial
constructors / events
概要: コンストラクタ/イベントにも partial
が使える。自動生成コードと手書きコードの分担が容易に。
Before
public partial class User
{
public User(string name) // 全実装を1か所にまとめるしかない
{
Name = name;
}
public event Action<string>? Changed; // 同上
public string Name { get; }
}
After
public partial class User
{
public partial User(string name);
public partial event Action<string> Changed;
}
// 別ファイルなどに実装
public partial class User
{
public string Name { get; }
public partial User(string name) => Name = name;
public partial event Action<string> Changed
{
add { /* 登録処理 */ }
remove { /* 解除処理 */ }
}
}
2-8. ユーザー定義複合代入演算子 (+=
など)
概要: operator +=
などを個別実装でき、+
と +=
の意味を揃えやすい。
Before
public readonly struct Money(decimal amount)
{
public decimal Amount { get; } = amount;
public static Money operator +(Money a, Money b) => new(a.Amount + b.Amount);
// += は C# が + を使って解決するが、明示的には定義不可
}
After
public readonly struct Money(decimal amount, string currency)
{
public decimal Amount { get; } = amount;
public string Currency { get; } = currency;
public static Money operator +(Money l, Money r)
=> l.Currency == r.Currency
? new Money(l.Amount + r.Amount, l.Currency)
: throw new InvalidOperationException();
public static Money operator +=(Money l, Money r) => l + r;
}
2-9. ユーザー定義 ++
/ --
概要: インクリメント/デクリメントもユーザー定義可。数値以外の"カウント可能"な概念にも自然な操作子を提供。
Before
// ++/-- はユーザー定義不可。Increment() などで代替
After
public struct Counter(int value)
{
public int Value { get; private set; } = value;
public static Counter operator ++(Counter c) => new(++c.Value);
public static Counter operator --(Counter c) => new(--c.Value);
}
3. 互換性と注意点
-
プレビュー機能は仕様変更の可能性あり。RTM 近くで細かい記法が変わることもあるため、
LangVersion
や SDK バージョンはプロジェクト単位で固定しておく -
field
は既存コードで変数名field
と衝突する可能性あり →@field
などで回避 - Extension members は"定義場所が分かりづらい"問題を悪化させる恐れ。命名規約・配置規約をチームで整備
4. 導入手順と段階的適用のコツ
-
スモールスタート: ユーティリティ/テストプロジェクトで
LangVersion=preview
を試す -
CI で SDK 固定:
global.json
などで .NET SDK バージョンをピン留め -
コード規約更新: Extension members の配置、
field
使用方針などを整備 -
RTM 後の再評価: 正式リリース後に差分確認し、
LangVersion=latest
へ切り替え
5. まとめ
- C# 14 は 「既存機能の穴埋め+表現力拡張」 によって、毎日のコーディングを一段と楽にしてくれるアップデート
- Before / After を見ると、記述量の削減 と 意図の明確化 が同時に進んでいることが分かるはず
- Preview 期間中に試し、チーム方針を固めておけば、RTM 後の移行がスムーズになります