📝

.NET 10 (C# 14) の新機能をまとめる

に公開
2

概要

[.NET 10 の新機能] https://learn.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-10/overview から、抜粋します。詳細はリンク先を参照してください。
本記事のコードは https://github.com/Kuroki-g/kuroki-g-public-zenn-code/tree/main/articles/dotnet10-feature にまとめてあります。

.NET ライブラリ

以下の内容をコードに起こしました。一部は省略しています。

グローバリゼーションと日付/時刻

文字列比較の数値の順序付け
StringComparer numericStringComparer = StringComparer.Create(
    System.Globalization.CultureInfo.CurrentCulture,
    System.Globalization.CompareOptions.NumericOrdering // NumericOrderingオプションが追加されました。
);

Console.WriteLine(numericStringComparer.Equals("02", "2")); // これはTrueと判定されます。

var sorted = new[] { "Windows XP", "Windows 10", "Windows 8", "Windows 11" }.Order(numericStringComparer);
// 10、11の数字が考慮され、以下の順番で出力されます:
// Windows 8
// Windows 10
// Windows 11
// Windows XP
foreach (string os in sorted)
{
    Console.WriteLine(os);
}

HashSet<string> set = new(numericStringComparer) { "007", "07", "7" };
Console.WriteLine(string.Join(",", set.ToArray())); // Output: 007 <- 順番が考慮されます。
Console.WriteLine(set.Contains("7")); // Output: True

HashSet<string> set2 = new(numericStringComparer) { "7", "007", "07",  };
Console.WriteLine(string.Join(",", set2.ToArray())); // Output: 7 <- 順番が考慮されます。
Console.WriteLine(set2.Contains("007")); // Output: True

シリアル化

JSON プロパティの重複を禁止するオプション
record MyRecord(int Value);

string json = """{ "Value": 1, "Value": -1 }""";
Console.WriteLine(JsonSerializer.Deserialize<MyRecord>(json)?.Value); // -1

// AllowDuplicatePropertiesを指定すると、例外となります。
JsonSerializerOptions options = new() { AllowDuplicateProperties = false };
try
{
    // JsonObject、Dictionary<string, int>でも同様にJsonExceptionとなります。
    JsonSerializer.Deserialize<MyRecord>(json, options);
}
catch (JsonException ex)
{
    Console.WriteLine(ex.Message);
}

// JsonDocumentでも同様に、AllowDuplicatePropertiesの設定が可能です。
JsonDocumentOptions docOptions = new() { AllowDuplicateProperties = false };
try
{
    JsonDocument.Parse(json, docOptions);
}
catch (JsonException ex)
{
    Console.WriteLine(ex.Message);
}
厳密な JSON シリアル化オプション
string json = """{ "Value": 1, "Value": -1 }""";
JsonSerializer.Deserialize<MyRecord>(json, JsonSerializerOptions.Strict);
// JsonSerializerOptions.Strict プリセットが追加されました。
// これは、以下のものに等しいです。
// JsonUnmappedMemberHandling.Disallow
// + JsonSerializerOptions.AllowDuplicateProperties = false
// + case sensitive (大文字と小文字の区別)
// + JsonSerializerOptions.RespectNullableAnnotations
// + JsonSerializerOptions.RespectRequiredConstructorParameters
// <https://github.com/dotnet/dotnet/blob/89c8f6a112d37d2ea8b77821e56d170a1bccdc5a/src/runtime/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs#L180>

その他

他、複数の追加があります。

  • System.Numerics
  • オプションの検証
  • 診断
  • ZIP ファイル
  • Windows プロセス管理
  • WebSocket の機能強化
  • TLS の機能強化

詳細は、[.NET 10 用 .NET ライブラリの新機能] https://learn.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-10/libraries を参照してください。

C# 14 の新機能

以下の内容をコードに起こしました。一部は省略しています。

拡張メンバー

namespace Dotnet10Feature.Extensions;

/// <summary>
/// 拡張メンバー
/// C# 14 では、 拡張メンバーを定義するための新しい構文が追加されています。 
/// </summary>
/// <see href="https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-14#extension-members"/> 
public static class Enumerable
{
    // 新しく、extensionブロックを用いて拡張メンバーを宣言することができるようになりました。
    // Extension block
    // https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/extension-methods#declare-extension-members
    extension<TSource>(IEnumerable<TSource> source) // extension members for IEnumerable<TSource>
    {
        // 新しく拡張プロパティの宣言ができるようになりました:
        public bool IsEmpty => !source.Any();

        // 拡張メソッドの宣言をextensionブロックに書くことができます:
        public IEnumerable<TSource> Where(Func<TSource, bool> predicate)
        {
            throw new NotImplementedException();
        }
    }

    // C# 14より前の場合、拡張プロパティはないのでメソッドで宣言する必要があります。
    public static bool BeforeC14IsEmpty<TSource>(this IEnumerable<TSource> source)
        => !source.Any();

    // 既存のC# 14より前のバージョンの拡張メソッドの宣言はこれまで通りです。
    public static IEnumerable<TSource> BeforeC14Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        throw new NotImplementedException();
    }

    // 静的メンバー + operatorのみの場合には、レシーバー型のみの、extensionブロックで表現することもできる。
    extension<TSource>(IEnumerable<TSource>)
    {
        // static拡張メソッド:
        public static IEnumerable<TSource> Combine(IEnumerable<TSource> first, IEnumerable<TSource> second)
        {
            throw new NotImplementedException();
        }

        // static拡張プロパティが定義可能です:
        public static IEnumerable<TSource> Identity => System.Linq.Enumerable.Empty<TSource>();

        // ユーザー定義のoperatorの定義ができるようになりました:
        // NOTE: [Extensions (拡張型) 未確認飛行 C]<https://ufcpp.net/blog/2024/3/extensions/> を見るに、しばらく前から要望があったようです。
        public static IEnumerable<TSource> operator +(IEnumerable<TSource> left, IEnumerable<TSource> right) => left.Concat(right);
    }
}

Null 条件付き割り当て

namespace Dotnet10Feature;

/// <summary>
/// Null 条件付き割り当て
/// </summary>
/// <see href="https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-14#null-conditional-assignment"/>
class NullConditionalAssignment
{
    void NullAssign(Customer? customer)
    {
        // Null check can be simplified (IDE0031) が出るようになりました。
        if (customer is not null)
        {
            customer.Order = GetCurrentOrder();
        }

        customer?.Order = GetCurrentOrder();
    }

    class Customer
    {
        public string? Order { get; set; } = null;
    }

    static string GetCurrentOrder()
    {
        return "CurrentOrder";
    }
}

nameof は、バインドされていないジェネリック型をサポートします

namespace Dotnet10Feature;

/// <summary>
/// バインドされていないジェネリック型と nameof
/// </summary>
/// <see href="https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-14#unbound-generic-types-and-nameof"/> 
public static class UnboundGenericTypesAndNameof
{
    public static void ShowExample()
    {
        // C# 14 以降では、 nameof する引数はバインドされていないジェネリック型にすることができます。
        var nameofList = nameof(List<>);
        // 以前のバージョンでは閉じたジェネリック型のみが使用可能でした。
        // NOTE: Use unbound generic type (IDE0340) の警告が出ます。
        var nameofListInt = nameof(List<int>);
    }
}

単純なラムダ パラメーターの修飾子

field でサポートされるプロパティ

/// <summary>
/// field キーワード
/// </summary>
/// <see href="https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-14#the-field-keyword"/>
class FieldFeature
{
    #region 
    // fieldキーワードを使用した新しい形式です。
    public string NewFormatProperty
    {
        get;
        // fieldキーワードを使うと簡略化することができます。
        // WARNING: `field`という名前のシンボルを含む型のコードがある場合には、ワークアラウンドが必要です。
        // NOTE: null許容参照の警告が出ます。
        set => field = value ?? throw new ArgumentNullException(nameof(value));
    }

    // 古い形式のプロパティ。VSCodeは、infoレベルでの警告となります。
    // 自動的に新しい形式に変更することが可能です。
    // NOTE: null許容参照の警告が出ます。
    private string _msg; // この形式の場合には、バッキングフィールドの宣言が必要です。

    public string OldFormatProperty
    {
        get => _msg;
        set => _msg = value ?? throw new ArgumentNullException(nameof(value));
    }
    #endregion
}

partial イベントとコンストラクター

namespace Dotnet10Feature;

public partial class MorePartialMembers
{
    // partial classが非常に長くなる場合に、このようにメソッドのみの宣言を事前にしておくことが可能です。
    // NOTE: staticは不可です。
    partial void PartialMethod(string s);

    // partial classで部分コンストラクターの宣言が可能になりました。
    // https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/constructors#partial-constructors
    public partial MorePartialMembers();
}

public partial class MorePartialMembers
{
    partial void PartialMethod(string s) => Console.WriteLine($"Something happened: {s}");

    public partial MorePartialMembers() // base()又はthis()の使用をする場合には、こちらに追加する必要があります。
    {
        // ここに実装宣言に追加することができます。   
    }
}

ユーザー定義複合代入演算子

namespace Dotnet10Feature;

/// <summary>
/// ユーザー定義複合割り当て
/// </summary>
/// <see href="https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/operator-overloading"/>
class UserDefinedCompoundAssignment
{
    class C1
    {
        public int Value;

        public static C1 operator +(C1 operand) => operand;

        public void operator +=(int x)
        {
            Value += x;
        }
    }
}

その他

  • Span<T> および ReadOnlySpan<T> のより暗黙的な変換

SDK と .NET 10 のツールの新機能

抜粋したものを記します。詳細は以下を参照してください。

slnx形式の対応

dotnet new slnの既定値が、SLNX ファイル形式になりました。
今まではプレビューでのみ利用可能でしたが、可読性が向上しました。

一回限りのツール実行

dotnet tool execはNode.jsにおける、npx相当の機能です。
.config/dotnet-tools.jsonがある場合には、それが考慮されます。

例えば、[CSharpier]https://www.nuget.org/packages/CSharpierを指定すると、以下のようになります。

$ dotnet tool exec CSharpier 
ツール パッケージ csharpier@1.2.1 がソース https://api.nuget.org/v3/index.json からダウンロードされます。
続行しますか? [y/n] (y):

新しい dnx ツールの実行スクリプト

使い心地はdnx = dotnet dnx = dotnet tool execです。

dotnet tool exec より引用:

  • dotnet dnx - dnx スクリプト自体を簡単に実装する方法として使用されるdotnet tool execの非表示のエイリアス
  • dnx - SDK から dotnet dnxを呼び出すシェル スクリプト。このスクリプトはインストーラーによって提供され、PATHで使用できます。 dnx <toolname>を介して直接ツールを簡単に使用できます。

NOTE: 新しい dnx ツールの実行スクリプト の説明が分かりにくいので、dotnet tool exec を見る方が良いです。

ファイルベースのアプリの機能強化

C# 14 および .NET 10 以降では、 ファイル ベースのアプリを作成出来るようになりました。

Console.WriteLine("Hello, world!");
$ dotnet run hello.cs
Hello, world!

NOTE: RC2時点では、[Announcing dotnet run app.cs – A simpler way o start with C# and .NET 10] https://devblogs.microsoft.com/dotnet/announcing-dotnet-run-app/ にのみ記されていたものが、[SDK と .NET 10 のツールの新機能]https://learn.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-10/sdkの一項目として、明確に記載されました。

フレームワークによって提供されるパッケージ参照の排除

.NET 9では、推移的な依存関係に対してでしたが、さらに強化され、直接の依存関係に対しても不要なパッケージ参照の排除が可能になりました。

より一貫性のあるコマンド順序

aliasが追加されました。

新しい名詞優先フォーム (New Noun-First Form) エイリアス (Alias)
dotnet package add dotnet add package
dotnet package list dotnet list package
dotnet package remove dotnet remove package
dotnet reference add dotnet add reference
dotnet reference list dotnet list reference
dotnet reference remove dotnet remove reference

ネイティブ シェルのタブ補完スクリプト

タブ補完が強化されました。タブ補完に用いるスクリプトを生成することができます。

dotnet completions script bash

タブ補完自体については、@nil2氏による、Bash用の補完スクリプトの作り方などを参照のこと。

その他

RuntimeIdentifiersを使う場合の使用感の向上

  • .NET ツールの機能強化

    • プラットフォーム固有の .NET ツール
    • プラットフォーム固有の .NET ツールで any RuntimeIdentifier を使用する
  • .NET Framework MSBuild で .NET MSBuild タスクを使用する

  • CLI コマンドは、対話式端末ではデフォルトで対話モードに設定されます

コンテナの機能の強化

  • コンソール アプリでコンテナー イメージをネイティブに作成できる
  • コンテナーのイメージ形式を明示的に制御する

Microsoft Testing Platformサポートの向上

  • dotnet test における Microsoft Testing Platform のサポート

.NET ランタイム

以下の改善・機能強化がなされたとのことです。
詳細は[.NET 10 ランタイムの新機能] https://learn.microsoft.com/ja-jp/dotnet/core/whats-new/dotnet-10/runtimeを参照してください。

  • JIT コンパイラの機能強化
  • AVX10.2 のサポート
  • スタックの割り当て
  • NativeAOT 型の事前初期化機能の改善
  • Arm64 書き込みバリアの機能強化

公式のコンテナイメージ

既定のイメージがDebianからUbuntuになりました。

mcr.microsoft.com/dotnet/sdk:10.0: Ubuntu 24.04 "Noble Numbat"

参考リンク

変更履歴

2025/11/14 コンテナイメージのセクションを追加
2025/11/14 .NET SDKの機能強化について更新
2025/11/06 拡張メンバーのコードが別のものになっていたため差し替えを実施

GitHubで編集を提案

Discussion