10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# 14 (Preview) 技術レポート — Before / After で分かる新機能まとめ

Posted at

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. 動かす準備(環境構築)

xml
<!-- .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 まで)

C#
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)

C#
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

C#
private string _message;
public string Message
{
    get => _message;
    set => _message = value ?? throw new ArgumentNullException(nameof(value));
}

After

C#
public string Message
{
    get;
    set => field = value ?? throw new ArgumentNullException(nameof(value));
}

2-3. Null 条件付き代入(左辺 ?. / ?[]

概要: 左辺に ?.?[] を使い、null なら代入も右辺評価もスキップ

使いどころ: 「オブジェクトがあれば処理」「なければ何もしない」を一行で書きたいとき。

Before

C#
if (customer != null)
{
    customer.Order = CreateOrder();
}

After

C#
customer?.Order = CreateOrder(); // null なら右辺も評価されない

List<string>? log = null;
log?.Add("OK");               // 何も起きない
(log ??= []).Add("init");     // null のときだけ初期化

2-4. nameof(Dictionary<,>):unbound generics 対応

概要: 型引数なしジェネリック型を nameof で扱える。

Before

C#
Console.WriteLine(typeof(Dictionary<,>).Name); // "Dictionary`2" などとなりがち

After

C#
Console.WriteLine(nameof(Dictionary<,>)); // "Dictionary"

2-5. Span 周りの暗黙変換強化

概要: Span<T> / ReadOnlySpan<T> と配列間の変換がより自然に。高パフォーマンスコードの記述が楽になる。

Before

C#
void Process(ReadOnlySpan<byte> data) { /* ... */ }

byte[] bytes = File.ReadAllBytes(path);
Process(bytes.AsSpan());          // 明示的な呼び出しが必要
ReadOnlySpan<byte> ro = bytes.AsSpan();

After

C#
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

C#
delegate bool TryParseInt(string s, out int result);
TryParseInt parse = (string text, out int result) => int.TryParse(text, out result);

After

C#
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

C#
public partial class User
{
    public User(string name)  // 全実装を1か所にまとめるしかない
    {
        Name = name;
    }

    public event Action<string>? Changed; // 同上
    public string Name { get; }
}

After

C#
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

C#
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

C#
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

C#
// ++/-- はユーザー定義不可。Increment() などで代替

After

C#
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. 導入手順と段階的適用のコツ

  1. スモールスタート: ユーティリティ/テストプロジェクトで LangVersion=preview を試す
  2. CI で SDK 固定: global.json などで .NET SDK バージョンをピン留め
  3. コード規約更新: Extension members の配置、field 使用方針などを整備
  4. RTM 後の再評価: 正式リリース後に差分確認し、LangVersion=latest へ切り替え

5. まとめ

  • C# 14 は 「既存機能の穴埋め+表現力拡張」 によって、毎日のコーディングを一段と楽にしてくれるアップデート
  • Before / After を見ると、記述量の削減意図の明確化 が同時に進んでいることが分かるはず
  • Preview 期間中に試し、チーム方針を固めておけば、RTM 後の移行がスムーズになります

6. 参考リンク

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?