.NET 8 の新機能

.NET 8 は .NET 7 の後継です。 長期サポート (LTS) リリースとして 3 年間サポートされます.NET 8 はこちらからダウンロードできます。

.NET Aspire

.NET Aspire は、オブザーバビリティに優れた、運用環境に適応できる分散アプリケーションの構築に役立つことを、とことん追求したクラウド向けスタックです。.NET Aspire は、クラウドネイティブの特定の懸念事項に対処する NuGet パッケージのコレクションによって提供されるもので、.NET 8 のプレビューで利用できます。 詳細については、「.NET Aspire (プレビュー)」を参照してください。

ASP.NET Core

ASP.NET Core の新機能については、「ASP.NET Core 8.0 の新機能」を参照してください。

Core .NET ライブラリ

このセクションでは、次のサブトピックについて説明します。

シリアル化

.NET 8 での System.Text.Json のシリアル化と逆シリアル化の機能に関しては、多くの機能強化が行われています。 たとえば、JSON ペイロードに含まれていないメンバーの処理をカスタマイズすることができます。

次のセクションでは、その他のシリアル化の機能強化について説明します。

一般的な JSON シリアル化の詳細については、.NET での JSON のシリアル化と逆シリアル化に関する記事を参照してください。

追加の型の組み込みサポート

シリアライザーには、次の追加の型のサポートが組み込まれています。

  • HalfInt128UInt128 の数値型。

    Console.WriteLine(JsonSerializer.Serialize(
        [ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ]
    ));
    // [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]
    
  • Memory<T> および ReadOnlyMemory<T> の値。 byte 値は Base64 文字列にシリアル化され、その他の型は JSON 配列にシリアル化されます。

    JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 }); // "AQID"
    JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); // [1,2,3]
    

ソース ジェネレーター

.NET 8 には、リフレクション ベースのシリアライザーと同等のネイティブ AOT エクスペリエンスを実現することを目的とした System.Text.Json ソース ジェネレーターの機能強化が含まれています。 次に例を示します。

  • ソース ジェネレーターで、requiredinit のプロパティを使用した型のシリアル化がサポートされるようになりました。 これらはどちらも、リフレクション ベースのシリアル化では既にサポートされています。

  • ソース生成コードの書式設定を改善しました。

  • JsonSourceGenerationOptionsAttribute 機能は JsonSerializerOptions と同等です。 詳細については、「オプションの指定 (ソースの生成)」を参照してください。

  • 追加の診断 (SYSLIB1034SYSLIB1039 など)。

  • 無視されるプロパティまたはアクセスできないプロパティの型は含めないでください。

  • 任意の型の種類での入れ子の JsonSerializerContext 宣言のサポート。

  • 弱く型指定されたソース生成シナリオでのコンパイラ生成型、または unspeakable 型のサポート。 コンパイラによって生成された型はソース ジェネレーターで明示的に指定できないため、実行時に最も近い先祖への解決が System.Text.Json で実行されるようになりました。 この解決により、値をシリアル化する最も適切なスーパータイプが決定されます。

  • 新しいコンバーター型 JsonStringEnumConverter<TEnum>。 既存の JsonStringEnumConverter クラスは、ネイティブ AOT ではサポートされていません。 列挙型には、次のように注釈を付けることができます。

    [JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
    public enum MyEnum { Value1, Value2, Value3 }
    
    [JsonSerializable(typeof(MyEnum))]
    public partial class MyContext : JsonSerializerContext { }
    

    詳細については、「列挙型フィールドを文字列としてシリアル化する」を参照してください。

  • 新しい JsonConverter.Type プロパティを使用すると、次のように非ジェネリックの JsonConverter インスタンスの型を検索できます。

    Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters)
        => converters.Where(converter => converter.Type != null)
                     .ToDictionary(converter => converter.Type!);
    

    このプロパティは JsonConverterFactory インスタンスに対しては null を、JsonConverter<T> インスタンスに対しては typeof(T) を返すので Null 許容です。

チェーン ソース ジェネレーター

JsonSerializerOptions クラスには、既存の TypeInfoResolver プロパティを補完する新しい TypeInfoResolverChain プロパティが含まれています。 これらのプロパティは、ソース ジェネレーターをチェーンするためのコントラクトのカスタマイズで使用されます。 新しいプロパティを追加すると、1 つの呼び出しサイトですべてのチェーン コンポーネントを指定する必要がなく、ファクトの後に追加できます。 TypeInfoResolverChain でも、チェーンをイントロスペクトしたり、そこからコンポーネントを削除したりできます。 詳細については、「ソース ジェネレーターを結合する」を参照してください。

さらに、JsonSerializerOptions.AddContext<TContext>() は廃止されました。 これは、TypeInfoResolverTypeInfoResolverChain のプロパティに置き換えられます。 詳細は、SYSLIB0049 を参照してください。

インターフェイス階層

.NET 8 ではインターフェイス階層からのプロパティのシリアル化のサポートが追加されます。

次のコードは、すぐに実装されたインターフェイスとその基本インターフェイスの両方のプロパティがシリアル化される例を示しています。

IDerived value = new DerivedImplement { Base = 0, Derived = 1 };
JsonSerializer.Serialize(value);
// {"Base":0,"Derived":1}

public interface IBase
{
    public int Base { get; set; }
}

public interface IDerived : IBase
{
    public int Derived { get; set; }
}

public class DerivedImplement : IDerived
{
    public int Base { get; set; }
    public int Derived { get; set; }
}

命名ポリシー

JsonNamingPolicy に、snake_case (アンダースコアあり) と kebab-case (ハイフンあり) のプロパティ名変換の新しい命名ポリシーが含まれています。 これらのポリシーは、既存の JsonNamingPolicy.CamelCase ポリシーと同様に使用します。

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
JsonSerializer.Serialize(new { PropertyName = "value" }, options);
// { "property_name" : "value" }

詳細については、「組み込みの名前付けポリシーを使用する」を参照してください。

読み取り専用プロパティ

読み取り専用のフィールドまたはプロパティ (set アクセサーを持たないもの) に逆シリアル化できるようになりました。

このサポートをグローバルにオプト インするには、新しいオプション PreferredObjectCreationHandlingJsonObjectCreationHandling.Populate に設定します。 互換性に問題がある場合は、プロパティが設定される特定の型または個々のプロパティに [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] 属性を配置することで、より詳細に機能を有効にすることもできます。

たとえば、2 つの読み取り専用プロパティを持つ CustomerInfo 型に逆シリアル化する次のコードを考えてみましょう。

using System.Text.Json;

CustomerInfo customer =
    JsonSerializer.Deserialize<CustomerInfo>("""
        {"Names":["John Doe"],"Company":{"Name":"Contoso"}}
        """)!;

Console.WriteLine(JsonSerializer.Serialize(customer));

class CompanyInfo
{
    public required string Name { get; set; }
    public string? PhoneNumber { get; set; }
}

[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class CustomerInfo
{
    // Both of these properties are read-only.
    public List<string> Names { get; } = new();
    public CompanyInfo Company { get; } = new()
    {
        Name = "N/A", PhoneNumber = "N/A"
    };
}

.NET 8 より前では、入力値は無視され、NamesCompany のプロパティは既定値を保持していました。

{"Names":[],"Company":{"Name":"N/A","PhoneNumber":"N/A"}}

今後は、入力値を使用して、逆シリアル化中に読み取り専用プロパティが設定されます。

{"Names":["John Doe"],"Company":{"Name":"Contoso","PhoneNumber":null}}

逆シリアル化の動作の "設定" については、「初期化されたプロパティを設定する」を参照してください。

リフレクションベースの既定値を無効にする

リフレクションベースのシリアライザーの使用を既定で無効にできるようになりました。 この無効化は、特にトリミングされたネイティブ AOT アプリで、使用されていないリフレクション コンポーネントが誤ってルート化されないようにするのに役立ちます。 JsonSerializerOptions 引数を JsonSerializer のシリアル化と逆シリアル化のメソッドに渡すことを要求して、既定のリフレクションベースのシリアル化を無効にするには、プロジェクト ファイルで JsonSerializerIsReflectionEnabledByDefault の MSBuild プロパティを false に設定します。

新しい IsReflectionEnabledByDefault API を使用して、機能スイッチの値をチェックします。 System.Text.Json の上に構築されているライブラリ作成者の場合は、このプロパティを使用して、リフレクション コンポーネントを誤ってルート化することなく、既定値を構成できます。

詳細については、「リフレクションの既定値を無効にする」を参照してください。

新しい JsonNode API メソッド

JsonNode および System.Text.Json.Nodes.JsonArray 型には、次の新しいメソッドが含まれています。

public partial class JsonNode
{
    // Creates a deep clone of the current node and all its descendants.
    public JsonNode DeepClone();

    // Returns true if the two nodes are equivalent JSON representations.
    public static bool DeepEquals(JsonNode? node1, JsonNode? node2);

    // Determines the JsonValueKind of the current node.
    public JsonValueKind GetValueKind(JsonSerializerOptions options = null);

    // If node is the value of a property in the parent
    // object, returns its name.
    // Throws InvalidOperationException otherwise.
    public string GetPropertyName();

    // If node is the element of a parent JsonArray,
    // returns its index.
    // Throws InvalidOperationException otherwise.
    public int GetElementIndex();

    // Replaces this instance with a new value,
    // updating the parent object/array accordingly.
    public void ReplaceWith<T>(T value);

    // Asynchronously parses a stream as UTF-8 encoded data
    // representing a single JSON value into a JsonNode.
    public static Task<JsonNode?> ParseAsync(
        Stream utf8Json,
        JsonNodeOptions? nodeOptions = null,
        JsonDocumentOptions documentOptions = default,
        CancellationToken cancellationToken = default);
}

public partial class JsonArray
{
    // Returns an IEnumerable<T> view of the current array.
    public IEnumerable<T> GetValues<T>();
}

非パブリック メンバー

JsonIncludeAttribute および JsonConstructorAttribute 属性注釈を使用して、特定の型のシリアル化コントラクトに非パブリック メンバーをオプトインできます。

string json = JsonSerializer.Serialize(new MyPoco(42));
// {"X":42}

JsonSerializer.Deserialize<MyPoco>(json);

public class MyPoco
{
    [JsonConstructor]
    internal MyPoco(int x) => X = x;

    [JsonInclude]
    internal int X { get; }
}

詳細については、「不変型と非パブリック メンバーおよびアクセサーを使用する」を参照してください。

ストリーミング逆シリアル化 API

.NET 8 には、GetFromJsonAsAsyncEnumerable などの新しい IAsyncEnumerable<T> ストリーミング逆シリアル化拡張メソッドが含まれています。 HttpClientJsonExtensions.GetFromJsonAsync など、Task<TResult> を返す同様のメソッドが存在しています。 新しい拡張メソッドはストリーミング API を呼び出し、IAsyncEnumerable<T> を返します。

次のコードは、新しい拡張メソッドを使用する方法を示しています。

const string RequestUri = "https://api.contoso.com/books";
using var client = new HttpClient();
IAsyncEnumerable<Book> books = client.GetFromJsonAsAsyncEnumerable<Book>(RequestUri);

await foreach (Book book in books)
{
    Console.WriteLine($"Read book '{book.title}'");
}

public record Book(int id, string title, string author, int publishedYear);

WithAddedModifier 拡張メソッド

新しいWithAddedModifier(IJsonTypeInfoResolver, Action<JsonTypeInfo>)拡張メソッドを使用すると、任意のインスタンスのシリアル化コントラクトに変更を簡単に導入できますIJsonTypeInfoResolver

var options = new JsonSerializerOptions
{
    TypeInfoResolver = MyContext.Default
        .WithAddedModifier(static typeInfo =>
        {
            foreach (JsonPropertyInfo prop in typeInfo.Properties)
            {
                prop.Name = prop.Name.ToUpperInvariant();
            }
        })
};

新しい JsonContent.Create オーバーロード

trim-safe またはソースで生成されたコントラクトを使用して、JsonContent インスタンスを作成できるようになりました。 新しいメソッドは次のとおりです。

var book = new Book(id: 42, "Title", "Author", publishedYear: 2023);
HttpContent content = JsonContent.Create(book, MyContext.Default.Book);

public record Book(int id, string title, string author, int publishedYear);

[JsonSerializable(typeof(Book))]
public partial class MyContext : JsonSerializerContext
{
}

JsonSerializerOptions インスタンスの固定

次の新しいメソッドを使用すると、JsonSerializerOptions インスタンスを固定するタイミングを制御できます。

  • JsonSerializerOptions.MakeReadOnly()

    このオーバーロードはトリム セーフに設計されているため、オプション インスタンスがリゾルバーで構成されていない場合は例外がスローされます。

  • JsonSerializerOptions.MakeReadOnly(Boolean)

    このオーバーロードに true を渡すと、オプション インスタンスに既定のリフレクション リゾルバーが設定されます (存在しない場合)。 このメソッドは RequiresUnreferenceCode/RequiresDynamicCode とマークされているため、ネイティブ AOT アプリケーションに適していません。

新しい IsReadOnly プロパティを使用すると、オプション インスタンスが固定されているかどうかを確認できます。

時間抽象化

新しい TimeProvider クラスと ITimer インターフェイスにより、"時間抽象化" 機能が追加され、テスト シナリオで時間をモックすることができます。 さらに、時間抽象化を使用して、Task.DelayTask.WaitAsync を使用して時間の進行に依存する Task 操作をモックすることができます。 時間抽象化では、次の基本的な時間操作がサポートされています。

  • ローカル時刻と UTC 時刻を取得する
  • パフォーマンスを測定するためのタイムスタンプを取得する
  • タイマーを作成する

次のコード スニペットは、いくつかの使用例を示しています。

// Get system time.
DateTimeOffset utcNow = TimeProvider.System.GetUtcNow();
DateTimeOffset localNow = TimeProvider.System.GetLocalNow();

// Create a time provider that works with a
// time zone that's different than the local time zone.
private class ZonedTimeProvider : TimeProvider
{
    private TimeZoneInfo _zoneInfo;

    public ZonedTimeProvider(TimeZoneInfo zoneInfo) : base()
    {
        _zoneInfo = zoneInfo ?? TimeZoneInfo.Local;
    }

    public override TimeZoneInfo LocalTimeZone => _zoneInfo;

    public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) =>
        new ZonedTimeProvider(zoneInfo);
}

// Create a timer using a time provider.
ITimer timer = timeProvider.CreateTimer(
    callBack, state, delay, Timeout.InfiniteTimeSpan);

// Measure a period using the system time provider.
long providerTimestamp1 = TimeProvider.System.GetTimestamp();
long providerTimestamp2 = TimeProvider.System.GetTimestamp();

var period = GetElapsedTime(providerTimestamp1, providerTimestamp2);

UTF8 の機能強化

型の文字列のような表現を宛先スパンに書き込めるようにするには、型に新しい IUtf8SpanFormattable インターフェイスを実装してください。 この新しいインターフェイスは ISpanFormattable と密接に関連していますが、UTF16 と Span<char> ではなく UTF8 と Span<byte> をターゲットにします。

IUtf8SpanFormattable は、stringSpan<char>Span<byte> のどれをターゲットにしているかに関係なく、まったく同じ共有ロジックを使用して、すべてのプリミティブ型 (および他の型) に実装されています。 すべての書式 (新しい "B" バイナリ指定子を含む) とすべてのカルチャが完全にサポートされています。 つまり、ByteComplexCharDateOnlyDateTimeDateTimeOffsetDecimalDoubleGuidHalfIPAddressIPNetworkInt16Int32Int64Int128IntPtrNFloatSByteSingleRuneTimeOnlyTimeSpanUInt16UInt32UInt64UInt128UIntPtrVersion から UTF8 に直接書式を設定できるようになりました。

新しい Utf8.TryWrite メソッドは、UTF16 ベースである既存の MemoryExtensions.TryWrite メソッドに対応する UTF8 ベースのメソッドを提供します。 補間された文字列の構文を使用すると、複雑な式を UTF8 バイトのスパンに直接書式設定できます。次に例を示します。

static bool FormatHexVersion(
    short major,
    short minor,
    short build,
    short revision,
    Span<byte> utf8Bytes,
    out int bytesWritten) =>
    Utf8.TryWrite(
        utf8Bytes,
        CultureInfo.InvariantCulture,
        $"{major:X4}.{minor:X4}.{build:X4}.{revision:X4}",
        out bytesWritten);

この実装は、書式の値の IUtf8SpanFormattable を認識し、その実装を使用して UTF8 表現を宛先スパンに直接書き込みます。

この実装は、新しい Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32) メソッドも利用します。これは、その Encoding.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32) に対応するものと共に、宛先スパンへのエンコードとデコードをサポートします。 スパンの長さが結果の状態を保持するのに十分でない場合、メソッドは例外をスローするのではなく false を返します。

ランダムに操作する方法

System.RandomSystem.Security.Cryptography.RandomNumberGenerator の型には、ランダムに操作するための 2 つの新しいメソッドが導入されています。

GetItems<T>()

新しい System.Random.GetItemsSystem.Security.Cryptography.RandomNumberGenerator.GetItems のメソッドを使用すると、入力セットから指定した数の項目をランダムに選択できます。 次の例は、(Random.Shared プロパティによって提供されるインスタンスで) System.Random.GetItems<T>() を使用して、配列に 31 個の項目をランダムに挿入する方法を示しています。 この例は、プレーヤーがボタンの色の順序を覚えておく必要がある "Simon" のゲームで使用できます。

private static ReadOnlySpan<Button> s_allButtons = new[]
{
    Button.Red,
    Button.Green,
    Button.Blue,
    Button.Yellow,
};

// ...

Button[] thisRound = Random.Shared.GetItems(s_allButtons, 31);
// Rest of game goes here ...

Shuffle<T>()

新しい Random.ShuffleRandomNumberGenerator.Shuffle<T>(Span<T>) のメソッドを使用すると、スパンの順序をランダム化できます。 これらのメソッドは、機械学習のトレーニングの偏りを減らすのに役立ちます (そのため、最初は必ずしもトレーニングではなく、最後は常にテストになります)。

YourType[] trainingData = LoadTrainingData();
Random.Shared.Shuffle(trainingData);

IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);

DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData);
model = chain.Fit(split.TrainSet);

IDataView predictions = model.Transform(split.TestSet);
// ...

パフォーマンスに重点を置いた型

.NET 8 では、アプリのパフォーマンスの向上を目的とした新しい型がいくつか導入されています。

  • 新しい System.Collections.Frozen の名前空間には、コレクション型 FrozenDictionary<TKey,TValue>FrozenSet<T> が含まれています。 これらの型では、コレクションの作成後にキーと値を変更することができません。 この要件により、読み取り操作を高速化できます (例: TryGetValue())。 これらの型は、最初の使用時に設定された後、有効期間の長いサービスで保持されるコレクションに特に役立ちます。次に例を示します。

    private static readonly FrozenDictionary<string, bool> s_configurationData =
        LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
    
    // ...
    if (s_configurationData.TryGetValue(key, out bool setting) && setting)
    {
        Process();
    }
    
  • 新しい System.Buffers.SearchValues<T> 型は、渡されたコレクション内の任意の値の最初の出現を検索するメソッドに渡されるように設計されています。 たとえば、String.IndexOfAny(Char[]) は呼び出された string 内の指定した配列内で任意の文字が最初に出現する箇所を検索します。 NET 8 では、String.IndexOfAnyMemoryExtensions.IndexOfAny などのメソッドの新しいオーバーロードが追加され、新しい型のインスタンスが受け入れられます。 System.Buffers.SearchValues<T> のインスタンスを作成すると、後続の検索を最適化するために必要なすべてのデータが "その時点で" 導出されるため、作業は前もって行われます。

  • 新しい System.Text.CompositeFormat 型は、コンパイル時に不明な書式指定文字列を最適化する場合に便利です (たとえば、書式指定文字列がリソース ファイルから読み込まれる場合など)。 文字列の解析などの作業を行うために、前もって多少の時間が費やされますが、使用のたびに作業が行われずに済みます。

    private static readonly CompositeFormat s_rangeMessage =
        CompositeFormat.Parse(LoadRangeMessageResource());
    
    // ...
    static string GetMessage(int min, int max) =>
        string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
    
  • 新しい System.IO.Hashing.XxHash3System.IO.Hashing.XxHash128 の型は、高速な XXH3 および XXH128 のハッシュ アルゴリズムの実装を提供します。

System.Numerics と System.Runtime.Intrinsics

このセクションでは、System.NumericsSystem.Runtime.Intrinsics の名前空間の機能強化について説明します。

  • Vector256<T>Matrix3x2、および Matrix4x4 では、.NET 8 のハードウェア アクセラレーションが改善されました。 たとえば、Vector256<T> が、必要に応じて内部的に 2x Vector128<T> 操作になるように再実装されました。 これにより、Arm64 などで、Vector256.IsHardwareAccelerated == false 以外の Vector128.IsHardwareAccelerated == true の場合に一部の関数を部分的に高速化できます。
  • ハードウェアの組み込みに、ConstExpected 属性で注釈が付けられるようになりました。 これにより、基になるハードウェアで定数が必要な場合や、定数以外の値が予期せずパフォーマンスを損なう可能性がある場合に、ユーザーが認識できるようになります。
  • Lerp(TSelf, TSelf, TSelf)Lerp API が IFloatingPointIeee754<TSelf> と、それに従い float (Single)、double (Double)、Half に追加されています。 この API を使用すると、2 つの値間の線形補間を効率的に正しく実行できます。

Vector512 と AVX-512

.NET Core 3.0 では、x86/x64 用のプラットフォーム固有のハードウェア組み込み API が含まれるよう SIMD サポートが拡張されました。 .NET 5 では Arm64 のサポートが追加され、.NET 7 ではクロスプラットフォーム ハードウェアの組み込みが追加されました。 .NET 8 ではさらに、Vector512<T>Intel Advanced Vector Extensions 512 (AVX-512) 命令のサポートが導入されたことで、SIMD のサポートがさらに強化されます。

具体的には、.NET 8 には、AVX-512 の次の主な機能のサポートが含まれています。

  • 512 ビット ベクター操作
  • 追加の 16 個の SIMD レジスタ
  • 128 ビット、256 ビット、512 ビットのベクターに使用できる追加の命令

この機能をサポートするハードウェアがある場合、Vector512.IsHardwareAcceleratedtrue と報告されるようになりました。

.NET 8 では、System.Runtime.Intrinsics.X86 名前空間の下にいくつかのプラットフォーム固有のクラスも追加されます。

これらのクラスは、64 ビット プロセスでのみ使用できる命令の IsSupported プロパティと入れ子になった Avx512F.X64 クラスを公開するという点で、他の命令セット アーキテクチャ (ISA) と同じ一般的な形状に従います。 さらに、各クラスには、対応する命令セットの Avx512VL (ベクター長) 拡張を公開する、入れ子になった Avx512F.VL クラスがあります。

コードで Vector512 固有または Avx512F 固有の命令を明示的に使用しない場合でも、新しい AVX-512 サポートの恩恵を受ける可能性があります。 JIT では、Vector128<T> または Vector256<T> を使用するときに、追加のレジスタと命令を暗黙的に利用できます。 基本クラス ライブラリは、Span<T>ReadOnlySpan<T>、およびプリミティブ型に対して公開されている多くの数学 API によって公開されるほとんどの操作で、これらのハードウェア組み込みを内部的に使用します。

データ検証

System.ComponentModel.DataAnnotations 名前空間には、クラウドネイティブ サービスの検証シナリオを対象とした新しいデータ検証属性が含まれています。 既存 DataAnnotations の検証コントロールは、フォーム上のフィールドなど、一般的な UI データ入力検証に対応していますが、新しい属性は、構成オプションなどのユーザー入力以外のデータを検証するように設計されています。 新しい属性に加えて、RangeAttribute および RequiredAttribute 型に新しいプロパティが追加されました。

新しい API 説明
RangeAttribute.MinimumIsExclusive
RangeAttribute.MaximumIsExclusive
境界を許容範囲に含めるかどうかを指定します。
System.ComponentModel.DataAnnotations.LengthAttribute 文字列またはコレクションの下限と上限の両方を指定します。 たとえば、[Length(10, 20)] コレクションには少なくとも 10 個の要素と最大 20 個の要素が必要です。
System.ComponentModel.DataAnnotations.Base64StringAttribute 文字列が有効な Base64 表現であることを検証します。
System.ComponentModel.DataAnnotations.AllowedValuesAttribute
System.ComponentModel.DataAnnotations.DeniedValuesAttribute
許可リストと拒否リストをそれぞれ指定します。 たとえば、「 [AllowedValues("apple", "banana", "mango")] 」のように入力します。

メトリック

新しい API を使うと、MeterInstrument オブジェクトの作成時に、キーと値のペア タグをそれらにアタッチできます。 公開されたメトリック測定のアグリゲーターでは、タグを使って集計値を区別できます。

var options = new MeterOptions("name")
{
    Version = "version",
    // Attach these tags to the created meter
    Tags = new TagList()
    {
        { "MeterKey1", "MeterValue1" },
        { "MeterKey2", "MeterValue2" }
    }
};

Meter meter = meterFactory.Create(options);

Instrument instrument = meter.CreateCounter<int>(
    "counter", null, null, new TagList() { { "counterKey1", "counterValue1" } }
);
instrument.Add(1);

新しい API には以下が含まれます。

暗号化

.NET 8 では、SHA-3 ハッシュ プリミティブのサポートが追加されています。 (SHA-3 は現在、OpenSSL 1.1.1 以降を備えた Linux と Windows 11 のビルド 25324 以降でサポートされています)。SHA-2 が利用可能な API では、SHA-3 の補完が提供されるようになりました。 これには、ハッシュの SHA3_256SHA3_384SHA3_512、HMAC の HMACSHA3_256HMACSHA3_384HMACSHA3_512、アルゴリズムが構成可能なハッシュの HashAlgorithmName.SHA3_256HashAlgorithmName.SHA3_384HashAlgorithmName.SHA3_512、RSA OAEP 暗号化の RSAEncryptionPadding.OaepSHA3_256RSAEncryptionPadding.OaepSHA3_384RSAEncryptionPadding.OaepSHA3_512 が含まれます。

次の例はプラットフォームが SHA-3 をサポートしているかどうかを判断する SHA3_256.IsSupported プロパティを含めた、API の使用方法を示しています。

// Hashing example
if (SHA3_256.IsSupported)
{
    byte[] hash = SHA3_256.HashData(dataToHash);
}
else
{
    // ...
}

// Signing example
if (SHA3_256.IsSupported)
{
     using ECDsa ec = ECDsa.Create(ECCurve.NamedCurves.nistP256);
     byte[] signature = ec.SignData(dataToBeSigned, HashAlgorithmName.SHA3_256);
}
else
{
    // ...
}

SHA-3 のサポートは現在のところ、暗号化プリミティブのサポートを目的としています。 より高レベルのコンストラクションとプロトコルで、最初から SHA-3 が完全にサポートされることは期待できません。 これらのプロトコルには、X.509 証明書、SignedXml および COSE が含まれます。

ネットワーキング

HTTPS プロキシのサポート

これまで、HttpClient がサポートしていたプロキシ タイプはすべて、HTTPS URI であっても、"中間者" がクライアントが接続しているサイトを確認できるようにしていました。 HttpClientHTTPS プロキシをサポートするようになりました。これにより、クライアントとプロキシの間に暗号化されたチャネルが作成され、すべての要求を完全なプライバシーで処理できるようになります。

HTTPS プロキシを有効にするには、all_proxy 環境変数を設定するか、WebProxy クラスを使用してプロキシをプログラムで制御します。

Unix: export all_proxy=https://x.x.x.x:3218 Windows: set all_proxy=https://x.x.x.x:3218

WebProxy クラスを使用してプロキシをプログラムで制御することもできます。

ストリームベースの ZipFile メソッド

.NET 8 には、ZipFile.CreateFromDirectory の新しいオーバーロードが含まれています。これを使用すると、ディレクトリに含まれるすべてのファイルを収集して ZIP 化してから、結果の ZIP ファイルを指定されたストリームに格納できます。 同様に、新しい ZipFile.ExtractToDirectory オーバーロードを使用すると、ZIP 化されたファイルを含むストリームを指定して、その内容をファイルシステムに展開できます。 新しいオーバーロードは次のとおりです。

namespace System.IO.Compression;

public static partial class ZipFile
{
    public static void CreateFromDirectory(
        string sourceDirectoryName, Stream destination);

    public static void CreateFromDirectory(
        string sourceDirectoryName,
        Stream destination,
        CompressionLevel compressionLevel,
        bool includeBaseDirectory);

    public static void CreateFromDirectory(
        string sourceDirectoryName,
        Stream destination,
        CompressionLevel compressionLevel,
        bool includeBaseDirectory,
    Encoding? entryNameEncoding);

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName) { }

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName, bool overwriteFiles) { }

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { }

    public static void ExtractToDirectory(
        Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { }
}

これらの新しい API は、ディスク領域が制約されている場合に役立つ可能性があります。これらでは中間ステップとしてディスクを使用する必要がないためです。

拡張機能ライブラリ

このセクションでは、次のサブトピックについて説明します。

キー付き DI サービス

キー付き依存関係挿入 (DI) サービスは、キーを使用して DI サービスを登録および取得する手段を提供します。 キーを使用すると、サービスの登録方法と利用方法を範囲指定できます。 新しい API の一部を次に示します。

次の例は、キー付き DI サービスの使用を示しています。

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<BigCacheConsumer>();
builder.Services.AddSingleton<SmallCacheConsumer>();

builder.Services.AddKeyedSingleton<IMemoryCache, BigCache>("big");
builder.Services.AddKeyedSingleton<IMemoryCache, SmallCache>("small");

var app = builder.Build();

app.MapGet("/big", (BigCacheConsumer data) => data.GetData());
app.MapGet("/small", (SmallCacheConsumer data) => data.GetData());

app.Run();

class BigCacheConsumer([FromKeyedServices("big")] IMemoryCache cache)
{
    public object? GetData() => cache.Get("data");
}

class SmallCacheConsumer(IKeyedServiceProvider keyedServiceProvider)
{
    public object? GetData() => keyedServiceProvider.GetRequiredKeyedService<IMemoryCache>("small");
}

詳細については、dotnet/runtime#64427 を参照してください。

ホスト型ライフサイクル サービス

ホスト型サービスには、アプリケーションのライフサイクル中に実行するためのオプションが追加されました。 IHostedServiceStartAsyncStopAsync を提供しましたが、IHostedLifecycleService は次の追加メソッドを提供するようになりました。

これらのメソッドは、それぞれ既存のポイントの前後で実行されます。

次の例は、新しい API の使用方法を示しています。

using Microsoft.Extensions.Hosting;

IHostBuilder hostBuilder = new HostBuilder();
hostBuilder.ConfigureServices(services =>
{
    services.AddHostedService<MyService>();
});

using (IHost host = hostBuilder.Build())
{
    await host.StartAsync();
}

public class MyService : IHostedLifecycleService
{
    public Task StartingAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
    public Task StartAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
    public Task StartedAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
    public Task StopAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
    public Task StoppedAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
    public Task StoppingAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
}

詳細については、dotnet/runtime#86511 を参照してください。

オプションの検証

ソース ジェネレーター

スタートアップのオーバーヘッドを減らし、検証機能セットを改善するために、検証ロジックを実装するソース コード ジェネレーターを導入しました。 次のコードは、モデルと検証コントロール クラスの例を示しています。

public class FirstModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P1 { get; set; } = string.Empty;

    [Microsoft.Extensions.Options.ValidateObjectMembers(
        typeof(SecondValidatorNoNamespace))]
    public SecondModelNoNamespace? P2 { get; set; }
}

public class SecondModelNoNamespace
{
    [Required]
    [MinLength(5)]
    public string P4 { get; set; } = string. Empty;
}

[OptionsValidator]
public partial class FirstValidatorNoNamespace 
    : IValidateOptions<FirstModelNoNamespace>
{
}

[OptionsValidator]
public partial class SecondValidatorNoNamespace 
    : IValidateOptions<SecondModelNoNamespace>
{
}

アプリが依存関係の注入を使用する場合は、次のコード例に示すように検証を注入できます。

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(
    builder.Configuration.GetSection(...));

builder.Services.AddSingleton<
    IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<
    IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>();

ValidateOptionsResultBuilder 型

.NET 8 では、ValidateOptionsResult オブジェクトの作成を容易にする ValidateOptionsResultBuilder 型が導入されています。 重要なのは、このビルダーを使用すると、複数のエラーを累積できることです。 以前は、IValidateOptions<TOptions>.Validate(String, TOptions) の実装を必要とする ValidateOptionsResult オブジェクトの作成は困難であり、階層化された検証エラーが発生する場合がありました。 複数のエラーが発生すると、多くの場合、検証プロセスは最初のエラーで停止していました。

次のコード スニペットは、ValidateOptionsResultBuilder の使用例を示しています。

ValidateOptionsResultBuilder builder = new();
builder.AddError("Error: invalid operation code");
builder.AddResult(ValidateOptionsResult.Fail("Invalid request parameters"));
builder.AddError("Malformed link", "Url");

// Build ValidateOptionsResult object has accumulating multiple errors.
ValidateOptionsResult result = builder.Build();

// Reset the builder to allow using it in new validation operation.
builder.Clear();

LoggerMessageAttribute コンストラクター

LoggerMessageAttribute では、追加のコンストラクター オーバーロードが提供されるようになりました。 以前は、パラメーターなしのコンストラクターか、すべてのパラメーター (イベント ID、ログ レベル、メッセージ) を必要とするコンストラクターのどちらかを選択する必要がありました。 新しいオーバーロードにより、少ないコードで必要なパラメーターを指定するための柔軟性が向上します。 イベント ID を指定しない場合、システムが自動的に生成を行います。

public LoggerMessageAttribute(LogLevel level, string message);
public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message);

拡張機能メトリック

IMeterFactory インターフェイス

依存関係挿入 (DI) コンテナーに新しい IMeterFactory インターフェイスを登録し、それを使って分離された方法で Meter オブジェクトを作成できます。

既定のメーター ファクトリの実装を使って、IMeterFactory を DI コンテナーに登録します。

// 'services' is the DI IServiceCollection.
services.AddMetrics();

その後、コンシューマーはメーター ファクトリを取得し、それを使って新しい Meter オブジェクトを作成できます。

IMeterFactory meterFactory = serviceProvider.GetRequiredService<IMeterFactory>();

MeterOptions options = new MeterOptions("MeterName")
{
    Version = "version",
};

Meter meter = meterFactory.Create(options);

MetricCollector<T> クラス

新しい MetricCollector<T> クラスを使用すると、メトリックの測定値をタイムスタンプと共に記録できます。 さらに、このクラスは、タイムスタンプを正確に生成するために任意の時間プロバイダーを使用できるという柔軟性を提供します。

const string CounterName = "MyCounter";

var now = DateTimeOffset.Now;

var timeProvider = new FakeTimeProvider(now);
using var meter = new Meter(Guid.NewGuid().ToString());
var counter = meter.CreateCounter<long>(CounterName);
using var collector = new MetricCollector<long>(counter, timeProvider);

Assert.Empty(collector.GetMeasurementSnapshot());
Assert.Null(collector.LastMeasurement);

counter. Add(3);

// Verify the update was recorded.
Assert.Equal(counter, collector.Instrument);
Assert.NotNull(collector.LastMeasurement);

Assert.Single(collector.GetMeasurementSnapshot());
Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement);
Assert.Equal(3, collector.LastMeasurement.Value);
Assert.Empty(collector.LastMeasurement.Tags);
Assert.Equal(now, collector.LastMeasurement.Timestamp);

System.Numerics.Tensors.TensorPrimitives

更新された System.Numerics.Tensors NuGet パッケージには、テンソル操作のサポートを追加する新しい TensorPrimitives 名前空間に API が含まれています。 テンソル プリミティブは、AI や機械学習のようなデータ集中型のワークロードを最適化します。

セマンティック検索や取得拡張生成 (RAG) などの AI ワークロードでは、関連データでプロンプトを拡張することで、ChatGPT などの大規模な言語モデルの自然言語機能が拡張されます。 これらのワークロードでは、質問に答えるために最も関連性の高いデータを見つけるためのコサイン類似性などのベクトルに対する操作が重要です。 System.Numerics.Tensors.TensorPrimitives パッケージは、ベクトル操作用の API を提供します。つまり、外部依存関係を取得したり、独自の実装を記述したりする必要はありません。

このパッケージは、System.Numerics.Tensors パッケージを置き換えます。

詳細については、「.NET 8 RC 2 の発表」というブログ記事を参照してください。

ガベージ コレクション

.NET 8 では、即座にメモリ制限を調整する機能が追加されています。 これは、需要が変化するクラウドサービスのシナリオで役立ちます。 コスト効率を高めるには、需要の変動に応じて、サービスがリソースの消費量をスケールアップおよびスケールダウンする必要があります。 サービスが需要の減少を検出すると、メモリ制限を減らすことでリソース消費量をスケールダウンできます。 以前は、ガベージ コレクター (GC) が変化を認識していなかったため、これが失敗し、新しい制限よりも多くのメモリが割り当てられる可能性がありました。 この変更により、RefreshMemoryLimit() API を呼び出して、新しいメモリ制限で GC を更新できます。

次のような注意すべきいくつかの制限事項があります。

  • 32 ビット プラットフォーム (Windows x86 や Linux ARM など) で、まだヒープ ハード制限がない場合、.NET は新しいものを確立できません。
  • API は、更新に失敗したことを示す 0 以外の状態コードを返す場合があります。 これは、スケールダウンが早すぎて、GC が操縦する余地がない場合に発生する可能性があります。 この場合は、GC.Collect(2, GCCollectionMode.Aggressive) を呼び出して現在のメモリ使用量を縮小してから、もう一度やり直してください。
  • GC が起動時にプロセスで処理できると思うサイズを超えてメモリ制限をスケールアップすると、RefreshMemoryLimit の呼び出しは成功しますが、制限として認識されるメモリよりも多くのメモリを使用することはできなくなります。

次のコード スニペットは、API を呼び出す方法を示しています。

GC.RefreshMemoryLimit();

メモリ制限に関連する GC の構成設定の一部を更新することもできます。 次のコード スニペットでは、ヒープ ハード制限が 100 メガバイト (MiB) に設定されます。

AppContext.SetData("GCHeapHardLimit", (ulong)100 * 1_024 * 1_024);
GC.RefreshMemoryLimit();

ハード制限が無効な場合 (例えば、ヒープのハード制限の割合が負の場合や、ハード制限が低すぎる場合など)、API によって InvalidOperationException がスローされる可能性があります。 これは、新しい AppData 設定が原因かもしくはコンテナー メモリ制限の変更の結果、更新によって設定されるヒープのハード制限が、既にコミットされているものより低い場合に発生する可能性があります。

構成バインド ソース ジェネレーター

.NET 8 では、ASP.NET Core で AOT およびトリムフレンドリな構成を提供するソース ジェネレーターが導入されています。 ジェネレーターは、以前から存在しているリフレクションベースの実装の代替手段です。

ソース ジェネレーターは、Configure(TOptions)BindGet 呼び出しに対してプローブを行い型情報を取得します。 プロジェクトでジェネレーターが有効になっている場合、コンパイラは、既存のリフレクション ベースのフレームワーク実装ではなく、生成されたメソッドを暗黙的に選択します。

ジェネレーターを使用するためにソース コードを変更する必要はありません。 AOT の Web アプリでは、既定で有効になっています。 その他のプロジェクトの種類の場合、ソース ジェネレーターは既定ではオフになっていますが、次のようにプロジェクト ファイルで EnableConfigurationBindingGenerator プロパティを true に設定することでオプトインできます。

<PropertyGroup>
    <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

次のコードは、バインダーの呼び出し例を示しています。

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IConfigurationSection section = builder.Configuration.GetSection("MyOptions");

// !! Configure call - to be replaced with source-gen'd implementation
builder.Services.Configure<MyOptions>(section);

// !! Get call - to be replaced with source-gen'd implementation
MyOptions options0 = section.Get<MyOptions>();

// !! Bind call - to be replaced with source-gen'd implementation
MyOptions options1 = new MyOptions();
section.Bind(options1);

WebApplication app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

public class MyOptions
{
    public int A { get; set; }
    public string S { get; set; }
    public byte[] Data { get; set; }
    public Dictionary<string, string> Values { get; set; }
    public List<MyClass> Values2 { get; set; }
}

public class MyClass
{
    public int SomethingElse { get; set; }
}

リフレクションの改善

関数ポインターは .NET 5 で導入されましたが、その時点では、対応するリフレクションのサポートは追加されませんでした。 関数ポインターで typeof またはリフレクション (たとえば、それぞれ typeof(delegate*<void>()) または FieldInfo.FieldType) を使用する場合 IntPtr が返されました。 .NET 8 以降では、代わりに System.Type オブジェクトが返されます。 この型は、呼び出し規約、戻り値の型、パラメーターなど、関数ポインター メタデータへのアクセスを提供します。

Note

関数への物理アドレスである関数ポインター インスタンスは、引き続き IntPtr として表されます。 リフレクションの種類のみが変更されました。

現在、新しい機能は CoreCLR ランタイムと MetadataLoadContext でのみ実装されています。

新しい API (IsFunctionPointerSystem.Reflection.PropertyInfoSystem.Reflection.FieldInfoSystem.Reflection.ParameterInfo など) が System.Type に追加されました。 次のコードは、リフレクションに新しい API の一部を使用する方法を示しています。

// Sample class that contains a function pointer field.
public unsafe class UClass
{
    public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void> _fp;
}

// ...

FieldInfo fieldInfo = typeof(UClass).GetField(nameof(UClass._fp));

// Obtain the function pointer type from a field.
Type fpType = fieldInfo.FieldType;

// New methods to determine if a type is a function pointer.
Console.WriteLine(
    $"IsFunctionPointer: {fpType.IsFunctionPointer}");
Console.WriteLine(
    $"IsUnmanagedFunctionPointer: {fpType.IsUnmanagedFunctionPointer}");

// New methods to obtain the return and parameter types.
Console.WriteLine($"Return type: {fpType.GetFunctionPointerReturnType()}");

foreach (Type parameterType in fpType.GetFunctionPointerParameterTypes())
{
    Console.WriteLine($"Parameter type: {parameterType}");
}

// Access to custom modifiers and calling conventions requires a "modified type".
Type modifiedType = fieldInfo.GetModifiedFieldType();

// A modified type forwards most members to its underlying type.
Type normalType = modifiedType.UnderlyingSystemType;

// New method to obtain the calling conventions.
foreach (Type callConv in modifiedType.GetFunctionPointerCallingConventions())
{
    Console.WriteLine($"Calling convention: {callConv}");
}

// New method to obtain the custom modifiers.
var modifiers =
    modifiedType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers();

foreach (Type modreq in modifiers)
{
    Console.WriteLine($"Required modifier for first parameter: {modreq}");
}

前の例では、次の出力が生成されます。

IsFunctionPointer: True
IsUnmanagedFunctionPointer: True
Return type: System.Void
Parameter type: System.Int32&
Calling convention: System.Runtime.CompilerServices.CallConvSuppressGCTransition
Calling convention: System.Runtime.CompilerServices.CallConvCdecl
Required modifier for first parameter: System.Runtime.InteropServices.InAttribute

ネイティブ AOT のサポート

ネイティブ AOT として発行するオプションは、.NET 7 で最初に導入されました。 ネイティブ AOT を使用してアプリを発行すると、ランタイムを必要とせず、すべてが 1 つのファイルに含まれている完全に自己完結型のバージョンのアプリが作成されます。 .NET 8 では、ネイティブ AOT 発行に次の機能強化が加えられます。

  • macOS の x64 および Arm64 のアーキテクチャのサポートが追加されています。

  • Linux のネイティブ AOT アプリのサイズが最大で 50% 小さくなりました。 次の表は、.NET 7 と .NET 8 の .NET ランタイム全体を含むネイティブ AOT で公開された "Hello World" アプリのサイズを示しています。

    オペレーティング システム .NET 7 .NET 8
    Linux x64 (-p:StripSymbols=true を含む) 3.76 MB 1.84 MB
    Windows x64 2.85 MB 1.77 MB
  • 最適化の設定 (サイズまたは速度) を指定できます。 既定では、コンパイラはアプリケーションのサイズに注意を払いながら、高速なコードを生成します。 ただし、<OptimizationPreference> MSBuild プロパティを使用して、どちらか一方に特化して最適化することができます。 詳細については、「AOT デプロイを最適化する」を参照してください。

コンソール アプリ テンプレート

既定のコンソール アプリ テンプレートに、すぐに使用できる AOT のサポートが含まれるようになりました。 AOT コンパイル用に構成されたプロジェクトを作成するには、単に dotnet new console --aot を実行します。 --aot によって追加されるプロジェクト構成には、次の 3 つの効果があります。

  • プロジェクトを (dotnet publish や Visual Studio などで) 発行するときに、ネイティブ AOT を使用してネイティブの自己完結型実行可能ファイルが生成されます。
  • トリミング、AOT、および単一ファイルの互換性アナライザーが有効になります。 これらのアナライザーは、プロジェクトの問題が発生する可能性がある部分 (存在する場合) を警告します。
  • AOT のデバッグ時エミュレーションが有効になるため、AOT コンパイルなしでプロジェクトをデバッグしても、AOT と同様のエクスペリエンスが得られます。 たとえば、AOT の注釈が付いていない (したがって、互換性アナライザーで見逃される) NuGet パッケージで System.Reflection.Emit を使用する場合、このエミュレーションを行うことで、AOT を使用してプロジェクトを発行しようとしても驚くことがありません。

ネイティブ AOT を備えた iOS 類似プラットフォームをターゲットとする

.NET 8 では、iOS 類似プラットフォームに対するネイティブ AOT サポートを有効にする作業を開始します。 次のプラットフォームで、ネイティブ AOT を使用して .NET iOS および .NET MAUI アプリケーションをビルドして実行できるようになりました。

  • ios
  • iossimulator
  • maccatalyst
  • tvos
  • tvossimulator

予備テストでは、Mono の代わりにネイティブ AOT を使用する .NET iOS アプリで、ディスク上のアプリ サイズが約 35% 縮小することが示されています。 .NET MAUI iOS アプリのディスク上のアプリ サイズは、最大 50% 縮小します。 さらに、起動速度も速くなります。 Mono と比べて、.NET iOS アプリの起動速度は約 28% 早く、.NET MAUI iOS アプリでは起動パフォーマンスは 約 50% 向上しています。 この .NET 8 のサポートは試験的なものであり、機能全体としての最初のステップにすぎません。 詳細については、「.NET 8 の .NET MAUI 関連のパフォーマンスの向上」のブログ記事を参照してください。

ネイティブ AOT サポートは、アプリのデプロイを目的としたオプトイン機能として利用できます。Mono は依然として、アプリの開発とデプロイの既定のランタイムです。 iOS デバイスでネイティブ AOT を使用して .NET MAUI アプリケーションをビルドして実行するため、.NET MAUI ワークロードのインストールには dotnet workload install maui を、アプリの作成には dotnet new maui -n HelloMaui を使用します。 次に、プロジェクト ファイルで MSBuild プロパティ PublishAottrue に設定します。

<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>

必要なプロパティを設定し、次の例に示すように dotnet publish を実行すると、ネイティブ AOT を使用してアプリがデプロイされます。

dotnet publish -f net8.0-ios -c Release -r ios-arm64  /t:Run

制限事項

すべての iOS 機能がネイティブ AOT と互換性を持つわけではありません。 同様に、iOS で一般的に使用されるすべてのライブラリが NativeAOT と互換性があるわけではありません。 また、既存のネイティブ AOT デプロイの制限事項に加えて、次の一覧に、iOS 類似プラットフォームをターゲットとする場合のその他の制限事項をいくつか示します。

  • ネイティブ AOT の使用は、アプリのデプロイ (dotnet publish) 中にのみ有効になります。
  • マネージド コードのデバッグは Mono でのみサポートされています。
  • .NET MAUI フレームワークとの互換性には制限があります。

パフォーマンスの向上

.NET 8 には、コード生成と Just-In-Time (JIT) コンパイルの機能強化が含まれています。

  • Arm64 のパフォーマンスの向上
  • SIMD の機能強化
  • AVX-512 ISA 拡張機能のサポート (「Vector512 および AVX-512」を参照)
  • クラウドネイティブの機能強化
  • JIT スループットの向上
  • ループと一般的な最適化
  • ThreadStaticAttribute でマークされたフィールドの最適化されたアクセス
  • 連続レジスタの割り当て。 Arm64 には、テーブル ベクター参照の 2 つの命令があり、そのタプル オペランド内のすべてのエンティティが連続レジスタに存在する必要があります。
  • JIT または NativeAOT で、コンパイル時にサイズを決定できる場合に、SIMD を使用して一部のメモリ操作 (比較、コピー、ゼロ化など) をアンロールおよび自動ベクター化できるようになりました。

さらに、動的なガイド付き最適化のプロファイル (PGO) が改善され、既定で有効にされるようになりました。 ランタイム構成オプションを使って有効にする必要はなくなりました。 動的 PGO は階層型コンパイルと連動して、階層 0 で配置される追加のインストルメンテーションに基づいて、コードをさらに最適化します。

動的な PGO では、平均してパフォーマンスが約 15% 向上します。 約 4600 テストのベンチマーク スイートでは、23% で 20% 以上のパフォーマンス向上が見られました。

Codegen 構造体の昇格

.NET 8 には、構造体変数を昇格させる JIT の機能を一般化する codegen 用の新しい物理プロモーション最適化パスが含まれています。 この最適化 (集約 スカラー置換とも呼ばれます) は、構造体変数のフィールドをプリミティブ変数に置き換えます。これにより、JIT はこれを推論してより正確に最適化できるようになります。

JIT はすでにこの最適化をサポートしていますが、次のようないくつかの大きな制限があります。

  • これは、フィールドが 4 つ以下の構造体でのみサポートされていました。
  • これは、各フィールドがプリミティブ型であるか、プリミティブ型をラップする単純な構造体の場合にのみサポートされていました。

物理的な昇格によりこれらの制限が取り除かれ、長年にわたる JIT の問題の多くが解決されます。

.NET MAUI

.NET 8 の .NET MAUI の新機能については、「.NET MAUI for .NET 8 の新機能」を参照してください。

.NET SDK

このセクションでは、次のサブトピックについて説明します。

CLI ベースのプロジェクト評価

MSBuild には、MSBuild のデータをスクリプトまたはツールに簡単に組み込むことができる新機能が含まれています。 次の新しいフラグは、dotnet publish などの CLI コマンドで使用して、CI パイプラインやその他の場所で使用するデータを取得するために使用できます。

フラグ 説明
--getProperty:<PROPERTYNAME> 指定した名前に MSBuild プロパティを取得します。
--getItem:<ITEMTYPE> 指定した型の MSBuild 項目を取得します。
--getTargetResults:<TARGETNAME> 指定したターゲットを実行して出力を取得します。

値は標準出力に書き込まれます。 次の例に示すように、複数または複雑な値が JSON として出力されます。

>dotnet publish --getProperty:OutputPath
bin\Release\net8.0\
>dotnet publish -p PublishProfile=DefaultContainer --getProperty:GeneratedContainerDigest --getProperty:GeneratedContainerConfiguration
{
  "Properties": {
    "GeneratedContainerDigest": "sha256:ef880a503bbabcb84bbb6a1aa9b41b36dc1ba08352e7cd91c0993646675174c4",
    "GeneratedContainerConfiguration": "{\u0022config\u0022:{\u0022ExposedPorts\u0022:{\u00228080/tcp\u0022:{}},\u0022Labels\u0022...}}"
  }
}
>dotnet publish -p PublishProfile=DefaultContainer --getItem:ContainerImageTags
{
  "Items": {
    "ContainerImageTags": [
      {
        "Identity": "latest",
        ...
    ]
  }
}

ターミナル ビルドの出力

dotnet build には、より最新化されたビルド出力を生成するための新しいオプションがあります。 この "ターミナル ロガー" 出力は、発生したプロジェクトに関するエラーをグループ化し、ターゲットが複数あるプロジェクトのさまざまなターゲット フレームワークをより適切に区別し、ビルドの実行内容に関するリアルタイムの情報を提供します。 新しい出力をオプト インするには、--tl オプションを使用します。 このオプションの詳細については、dotnet build のオプションに関する記事を参照してください。

簡略化された出力パス

.NET 8 では、ビルド出力の出力パスとフォルダー構造を簡略化するオプションが導入されています。 以前の .NET アプリでは、さまざまなビルド成果物に対して深くて複雑な出力パスのセットが生成されていました。 新しい簡略された出力パス構造では、すべてのビルド出力が共通の場所に収集されるため、ツールでの予測が容易になります。

詳細については、「成果物の出力レイアウト」を参照してください。

dotnet workload clean コマンド

.NET 8 では、いくつかの .NET SDK または Visual Studio の更新で残された可能性のあるワークロード パックをクリーンアップする新しいコマンドが導入されています。 ワークロードを管理する際に問題が発生した場合、再試行する前に、workload clean を使用して既知の状態に安全に復元することを検討してください。 このコマンドには、2 つのモードがあります。

  • dotnet workload clean

    ファイルベースまたは MSI ベースのワークロードに対してワークロード ガベージ コレクションを実行し、孤立したパックをクリーンアップします。 孤立したパックとは、アンインストールされたバージョンの .NET SDK からのもの、またはパックのインストール レコードが存在しないパックです。

    Visual Studio がインストールされている場合、このコマンドは、Visual Studio を使用して手動でクリーンアップする必要があるワークロードの一覧も表示します。

  • dotnet workload clean --all

    このモードの方がアグレッシブであり、マシン上にある現在の SDK ワークロード インストールの種類である (および Visual Studio からのものではない) すべてのパックがクリーンアップされます。 また、実行中の .NET SDK 機能バンド以下のすべてのワークロード インストール レコードも削除されます。

dotnet publish および dotnet pack アセット

dotnet publishdotnet pack のコマンドは実稼働アセットの生成を目的としているため、既定で Release アセットが生成されるようになりました。

次の出力は、dotnet builddotnet publish の動作の違いと、PublishRelease プロパティを false に設定して Debug アセットの発行に戻す方法を示しています。

/app# dotnet new console
/app# dotnet build
  app -> /app/bin/Debug/net8.0/app.dll
/app# dotnet publish
  app -> /app/bin/Release/net8.0/app.dll
  app -> /app/bin/Release/net8.0/publish/
/app# dotnet publish -p:PublishRelease=false
  app -> /app/bin/Debug/net8.0/app.dll
  app -> /app/bin/Debug/net8.0/publish/

詳細については、「'dotnet pack' では Release 構成が使用される」と「'dotnet publish' では Release 構成が使用される」を参照してください。

dotnet restore セキュリティ監査

.NET 8 以降では、依存関係パッケージが復元されるときに、既知の脆弱性のセキュリティ チェックをオプト インできます。 この監査では、影響を受けるパッケージ名、脆弱性の重大度、詳細に関するアドバイザリへのリンクを含むセキュリティ脆弱性のレポートが生成されます。 dotnet add または dotnet restore を実行すると、検出された脆弱性に関する警告 NU1901-NU1904 が表示されます。 詳細については、「セキュリティの脆弱性の監査」を参照してください。

テンプレート エンジン

テンプレート エンジンは、NuGet のセキュリティ関連機能の一部を統合することで、.NET 8 でより安全なエクスペリエンスを提供します。 WebJobs からの改善点は、以下のとおりです。

  • 既定で、http:// フィードからのパッケージのダウンロードが回避されます。 たとえば、次のコマンドでは、ソース URL が HTTPS を使用しないため、テンプレート パッケージのインストールに失敗します。

    dotnet new install console --add-source "http://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"

    この制限は、--force フラグを使用してオーバーライドできます。

  • dotnet newdotnet new installdotnet new update では、テンプレート パッケージの既知の脆弱性のチェックがあります。 脆弱性が見つかっても続行する場合は、--force フラグを使用する必要があります。

  • dotnet new では、テンプレート パッケージの所有者に関する情報を指定してください。 所有権は NuGet ポータルによって検証され、信頼できる特性と見なすことができます。

  • dotnet searchdotnet uninstall では、"信頼済み" のパッケージからテンプレートがインストールされているかどうかを示します。つまり、予約済みのプレフィックスが使用されます。

Source Link が .NET SDK に含まれるようになりました。 目的は、Source Link を SDK にバンドルすることで、パッケージ用に別の <PackageReference> を必要とする代わりに、既定でこの情報を含めるパッケージが増えるようにすることです。 この情報により、開発者向けの IDE のエクスペリエンスが向上します。

ソース ビルド SDK

Linux ディストリビューション ビルド済み (ソース ビルド) SDK に、ソース ビルド ランタイム パッケージを使用して自己完結型アプリケーションをビルドする機能が追加されました。 ディストリビューション固有のランタイム パッケージが、ソース ビルド SDK にバンドルされています。 自己完結型のデプロイの間に、このバンドルされたランタイム パッケージが参照され、ユーザーに対してこの機能が有効になります。

グローバリゼーション

iOS/tvOS/MacCatalyst での HybridGlobalization モード

モバイル アプリは、より軽量な ICU バンドルを使用する新しいハイブリッド グローバリゼーション モードを使用できるようになりました。 ハイブリッド モードでは、グローバリゼーション データの一部は ICU バンドルから、一部はネイティブ API への呼び出しから取得されます。 モバイルでサポートされているすべてのロケールに対応します。

HybridGlobalization は、InvariantGlobalization モードで動作できないアプリや、モバイル上の ICU データからトリミングされたカルチャを使うアプリに最適です。 より小さい ICU データ ファイルを読み込む場合にも使用できます。 (icudt_hybrid.dat ファイルは、既定の ICU データ ファイル icudt.dat より 34.5% 小さくなります)。

HybridGlobalization モードを使用するには、MSBuild プロパティを true に設定します。

<PropertyGroup>
  <HybridGlobalization>true</HybridGlobalization>
</PropertyGroup>

注意すべきいくつかの制限事項があります。

  • ネイティブ API の制限により、すべてのグローバリゼーション API がハイブリッド モードでサポートされるわけではありません。
  • サポートされている API の一部は動作が異なります。

アプリケーションが影響を受けないことを確認するには、「動作の違い」を参照してください。

コンテナー

コンテナー イメージ

.NET 8 の .NET コンテナー イメージに対して、次の変更が行われました。

生成されたイメージの既定値

Microsoft .NET コンテナーの新しい non-root 機能が既定になり、これはアプリが既定でセキュリティで保護された状態であることに役立ちます。 独自の ContainerUser を設定することで、この既定値はいつでも変更できます。

既定のコンテナー タグは latest になりました。 この既定値は、コンテナー領域の他のツールと一致するもので、コンテナーを内部開発ループ内で使用することを容易にします。

Debian 12

コンテナー イメージで Debian 12 (Bookworm) が使用されるようになりました。 Debian は、.NET コンテナー イメージの既定の Linux ディストリビューションです。

非ルート ユーザー

イメージには non-root ユーザーが含まれます。 このユーザーが、イメージ non-root を使用できるようにします。 non-root として実行するには、Dockerfile の末尾に次の行 (または Kubernetes マニフェストで同様の命令) を追加します。

USER app

.NET 8 では、non-root ユーザーの UID の環境変数が追加されています。これは 64198 です。 この環境変数は、コンテナー ユーザーを名前ではなく UID で設定する必要がある Kubernetes runAsNonRoot テストに役立ちます。 この dockerfile は、使用例を示しています。

既定のポートもポート 80 から 8080 に変更されました。 この変更をサポートするために、ポートの変更を容易にするための新しい環境変数 ASPNETCORE_HTTP_PORTS を使用できます。 この変数は、ポートの一覧を受け入れます。これは、ASPNETCORE_URLS で必要な書式よりも簡単です。 これらの変数のいずれかを使用してポートをポート 80 に戻した場合は、non-root として実行できません。

Chiseled Ubuntu イメージ

.NET 8 では Chiseled Ubuntu イメージを使用できます。 Chiseled イメージは、非常に小さく、パッケージ マネージャーやシェルがなく、non-root であるため、攻撃対象領域が減少します。 この種類のイメージは、アプライアンススタイルのコンピューティングの利点を必要とする開発者向けです。 Chiseled イメージは、.NET の夜間成果物レジストリに発行されます。

マルチプラットフォーム コンテナー イメージをビルドする

Docker では、複数の環境間で動作するマルチプラットフォーム イメージの使用とビルドがサポートされています。 .NET 8 では、アーキテクチャを組み合わせて、ビルドする .NET イメージと一致させることができる新しいパターンが導入されています。 たとえば、macOS を使用しており、Azure で x64 クラウド サービスをターゲットにする場合、次のように --platform スイッチを使用してイメージをビルドできます。

docker build --pull -t app --platform linux/amd64

.NET SDK では、復元時に$TARGETARCH 値と -a 引数がサポートされるようになりました。 次のコード スニペットは、例を示しています。

RUN dotnet restore -a $TARGETARCH

# Copy everything else and build app.
COPY aspnetapp/. .
RUN dotnet publish -a $TARGETARCH --self-contained false --no-restore -o /app

詳細については、ブログ記事「マルチプラットフォーム コンテナーのサポートの改善」を参照してください。

ASP.NET 複合イメージ

コンテナー化のパフォーマンスを向上させる取り組みの一環として、ランタイムの複合バージョンを含む新しい ASP.NET Docker イメージが利用可能です。 この複合は、複数の MSIL アセンブリを 1 つのすぐに実行できる (R2R) 出力バイナリにコンパイルすることによってビルドされます。 これらのアセンブリは 1 つのイメージに埋め込まれているため、JIT 処理にかかる時間が短縮され、アプリの起動パフォーマンスが向上します。 通常の ASP.NET イメージと比較した複合イメージのもう 1 つの大きな利点は、ディスク上のサイズが小さいことです。

注意すべき点があります。 複合では複数のアセンブリが 1 つに埋め込まれているので、バージョンがより緊密に結合しています。 アプリでは、カスタム バージョンのフレームワークまたは ASP.NET バイナリを使用できません。

Alpine Linux、Jammy Chiseled、Mariner Distroless プラットフォームの複合イメージは、mcr.microsoft.com/dotnet/nightly/aspnet リポジトリから入手できます。 タグは、ASP.NET Docker ページ-composite サフィックスと共に一覧表示されます。

コンテナーの発行

パフォーマンスと互換性

.NET 8 では、リモート レジストリ (特に Azure レジストリ) にコンテナーをプッシュするためのパフォーマンスが向上しました。 高速化の理由は、1 つの操作における複数レイヤーのプッシュと、アトミック アップロードをサポートしていないレジストリ向けのより信頼性の高いチャンク メカニズムです。

これらの改善は、より多くのレジストリ (Harbor、Artifactory、Quay.io、Podman) がサポートされることも意味しています。

認証

.NET 8 では、コンテナーをレジストリにプッシュするときに、OAuth トークン交換認証 (Azure マネージド ID) のサポートが追加されます。 このサポートは、認証エラーなしで Azure Container Registry などのレジストリにプッシュできるようになったことを意味します。 次のコマンドは、発行フローの例を示しています。

> az acr login -n <your registry name>
> dotnet publish -r linux-x64 -p PublishProfile=DefaultContainer

.NET アプリのコンテナー化の詳細については、「dotnet publish を使用して .NET アプリをコンテナー化する」を参照してください。

tar.gz アーカイブに発行する

.NET 8 以降では、tar.gz アーカイブとしてコンテナーを直接作成できます。 この機能は、たとえば画像をプッシュする前にスキャン ツールを実行する必要がある場合などワークフローが複雑な場合に便利です。 アーカイブが作成されたら、アーカイブの移動、スキャン、またはローカル Docker ツールチェーンへの読み込みを行うことができます。

アーカイブに発行するには、次に例を示す ContainerArchiveOutputPath プロパティを dotnet publish コマンドに追加します。

dotnet publish \
  -p PublishProfile=DefaultContainer \
  -p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz

フォルダー名または特定のファイル名を持つパスを指定できます。

ソース生成 COM 相互運用

.NET 8 には、COM インターフェイスとの相互運用をサポートする新しいソース ジェネレーターが含まれています。 GeneratedComInterfaceAttribute を使用して、インターフェイスをソース ジェネレーターの COM インターフェイスとしてマークできます。 その後、ソース ジェネレーターは C# コードからアンマネージド コードへの呼び出しを可能にするコードを生成します。 また、アンマネージ コードから C# への呼び出しを可能にするコードも生成します。 このソース ジェネレーターは LibraryImportAttribute と統合されており、GeneratedComInterfaceAttribute を持つ型を LibraryImport 属性付きメソッドのパラメータおよび戻り値の型として使用できます。

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
interface IComInterface
{
    void DoWork();
}

internal class MyNativeLib
{
    [LibraryImport(nameof(MyNativeLib))]
    public static partial void GetComInterface(out IComInterface comInterface);
}

ソース ジェネレーターは新しい GeneratedComClassAttribute 属性もサポートしており、GeneratedComInterfaceAttribute 属性を持つインターフェイスを実装する型をアンマネージド コードに渡すことを可能にします。 ソース ジェネレーターは、このインターフェイスを実装し、マネージド実装への呼び出しを転送する COM オブジェクトを公開するために必要なコードを生成します。

GeneratedComInterfaceAttribute 属性を持つインターフェイス上のメソッドは LibraryImportAttribute と同じ型をすべてサポートしており、LibraryImportAttributeGeneratedComInterface 属性付きの型と GeneratedComClass 属性付きの型をサポートするようになりました。

C# コードが GeneratedComInterface 属性付きのインターフェイスのみを使用して、アンマネージ コードからの COM オブジェクトをラップするか、C# からのマネージド オブジェクトをラップして、アンマネージド コードに公開する場合は、Options プロパティのオプションを使用して、どのコードが生成されるかをカスタマイズできます。 これらのオプションによって、使用されないことがわかっているシナリオにマーシャラーを記述する必要がなくなります。

ソース ジェネレーターは、新しい StrategyBasedComWrappers 型を使用して COM オブジェクト ラッパーとマネージド オブジェクト ラッパーを作成および管理します。 この新しい型は、COM 相互運用で期待される .NET ユーザー エクスペリエンスを提供するだけでなく、高度なユーザーにカスタマイズ ポイントを提供します。 アプリケーションに COM からの型を定義するための独自のメカニズムがある場合、またはソース生成 COM で現在サポートされていないシナリオをサポートする必要がある場合は、新しい StrategyBasedComWrappers 型を使用してシナリオに不足している機能を追加し、COM 型に対する同じ .NET ユーザー エクスペリエンスを得ることを検討してください。

Visual Studio を使用している場合、新しいアナライザーとコード修正により、既存の COM 相互運用コードを変換してソース生成相互運用を使用することが容易になります。 ComImportAttribute を持つ各インターフェイスの横の電球アイコンには、ソース生成相互運用に変換するオプションが用意されています。 修正により、GeneratedComInterfaceAttribute 属性を使用するようにインターフェイスが変更されます。 GeneratedComInterfaceAttribute を持つインターフェイスを実装するすべてのクラスの横の電球アイコンには、GeneratedComClassAttribute 属性を型に追加するオプションが容易されています。 型が変換されたら、LibraryImportAttribute を使用するように DllImport メソッドを移動できます。

制限事項

COM ソース ジェネレーターでは、アパートメント アフィニティ、COM CoClass をアクティブにするための new キーワードの使用、および次に示す API はサポートされていません。

  • IDispatch ベースのインターフェイス。
  • IInspectable ベースのインターフェイス。
  • COM プロパティおよびイベント。

Linux 上の .NET

Linux の最小サポート ベースライン

.NET 8 では Linux の最小サポート ベースラインが更新されました。 .NET は、すべてのアーキテクチャで Ubuntu 16.04 を対象に構築されます。 これは主に、.NET 8 で最小の glibc バージョンを定義する場合に重要です。 .NET 8 は、Ubuntu 14.04 や Red Hat Enterprise Linux 7 などの古い glibc を含むディストリビューション バージョンでは起動できません。

詳細については、「Red Hat Enterprise Linux ファミリのサポート」を参照してください。

Linux で独自の .NET をビルドする

以前の .NET バージョンでは、ソースから .NET をビルドできましたが、リリースに対応する dotnet/installer リポジトリ コミットから "source tarball" を作成する必要がありました。 .NET 8 では、これが不要になり、Linux 上の .NET を dotnet/dotnet リポジトリから直接ビルドできます。 このリポジトリでは dotnet/source-build を使用して .NET ランタイム、ツール、SDK をビルドします。 これは、Red Hat と Canonical が .NET のビルドに使用するビルドと同じです。

dotnet-buildtools/prereqs コンテナー イメージには必要なすべての依存関係が含まれるため、コンテナーでビルドする方法がほとんどのユーザーにとって最も簡単です。 詳細については、ビルド手順に関する記事を参照してください。

クロスビルド Windows アプリ

Windows 以外のプラットフォームで Windows をターゲットとするアプリをビルドすると、結果の実行可能ファイルは、指定した Win32 リソース (アプリケーションのアイコン、マニフェスト、バージョン情報など) で更新されるようになりました。

以前は、このようなリソースを使用するには、Windows 上にアプリケーションを構築する必要がありました。 このクロスビルディング サポートのギャップを解決することは、インフラストラクチャの複雑さとリソースの使用の両方に影響を与える重大な問題であったため、よく聞かれる要望でした。

Android アプリの AOT コンパイル

アプリのサイズを小さくするために、Android を対象とする .NET アプリと .NET MAUI アプリは、リリース モードでビルドされるときに、"プロファイリングされた" 事前 (AOT) コンパイル モードを使用します。 プロファイリングされた AOT コンパイルは、通常の AOT コンパイルよりも少ないメソッドに影響します。 .NET 8 では、Android アプリのサイズをさらに小さくするためにさらに AOT コンパイルを選択できる <AndroidStripILAfterAOT> プロパティが導入されました。

<PropertyGroup>
  <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
</PropertyGroup>

既定では、AndroidStripILAfterAOTtrue に設定すると、既定の AndroidEnableProfiledAot 設定がオーバーライドされ、AOT コンパイルされた (ほぼ) すべてのメソッドをトリミングできます。 また、両方のプロパティを true に明示的に設定することで、プロファイリングされた AOT と IL のストリッピングをまとめて使用することもできます。

<PropertyGroup>
  <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
  <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>

コード分析

.NET 8 には、.NET ライブラリ API の使い方が正しく効率的であることを検証するのに役立つ、いくつかの新しいコード アナライザーと修正ツールが含まれています。 次の表は新しいアナライザーをまとめたものです。

ルールの ID カテゴリ 説明
CA1856 パフォーマンス ConstantExpectedAttribute 属性がパラメーターに正しく適用されていない場合に発生します。
CA1857 パフォーマンス ConstantExpectedAttribute でパラメーターに注釈が付けられているのに、指定された引数が定数ではない場合に発生します。
CA1858 パフォーマンス 文字列が特定のプレフィックスで始まっているかどうかを確認するには、String.IndexOf を呼び出して結果を 0 と比較するより、String.StartsWith を呼び出す方が良い方法です。
CA1859 パフォーマンス この規則では、可能であれば、特定のローカル変数、フィールド、プロパティ、メソッド パラメーター、およびメソッドの戻り値の型をインターフェイス型または抽象型から具象型にアップグレードすることを勧めます。 具象型を使うと、生成されるコードの品質が向上します。
CA1860 パフォーマンス コレクション型に要素があるかどうかを確認するには、Enumerable.Any を呼び出すより、LengthCount、または IsEmpty を使うことをお勧めします。
CA1861 パフォーマンス 引数として渡される定数配列は、繰り返し呼び出された場合に再利用されません。これは、新しい配列が毎回作成されることを意味します。 パフォーマンスを向上させるには、配列を静的な読み取り専用フィールドに抽出することを検討してください。
CA1865-CA1867 パフォーマンス char オーバーロードは、単一の char を含む文字列のパフォーマンスに優れたオーバーロードです。
CA2021 [信頼性] Enumerable.Cast<TResult>(IEnumerable)Enumerable.OfType<TResult>(IEnumerable) が正しく機能するには、互換性のある型が必要です。 拡大変換とユーザー定義変換は、ジェネリック型ではサポートされていません。
CA1510 - CA1513 保守容易性 スロー ヘルパーは、新しい例外インスタンスを構築する if ブロックより簡単で効率的です。 これら 4 つのアナライザーは、次の例外に対して作成されました: ArgumentNullExceptionArgumentExceptionArgumentOutOfRangeExceptionObjectDisposedException

Windows Presentation Foundation

ハードウェアの高速化

以前は、リモートからアクセスされたすべての WPF アプリケーションでは、システムにハードウェア レンダリング機能がある場合でも、ソフトウェア レンダリングを使用する必要がありました。 .NET 8 では、リモート デスクトップ プロトコル (RDP) のハードウェア アクセラレーションを選択できるオプションが追加されています。

ハードウェア アクセラレーションとは、コンピューターのグラフィックス処理装置 (GPU) を使って、アプリケーションのグラフィックスと視覚効果のレンダリングを高速化することを指します。 これにより、パフォーマンスの向上と、よりシームレスで応答性の高いグラフィックスを実現できます。 これに対し、ソフトウェア レンダリングは、コンピューターの中央処理装置 (CPU) のみに依存してグラフィックスをレンダリングします。グラフィックスのレンダリングは遅くなり、効果が低下する可能性があります。

オプトインするには、Switch.System.Windows.Media.EnableHardwareAccelerationInRdp 構成プロパティを runtimeconfig.json ファイルで true に設定します。 詳細については、「RDP のハードウェア アクセラレーション」を参照してください。

OpenFolderDialog

WPF には、OpenFolderDialog と呼ばれる新しいダイアログ ボックス コントロールが含まれています。 このコントロールを使用すると、アプリ ユーザーはフォルダーを参照して選択できます。 以前は、アプリ開発者はこの機能を実現するためにサードパーティ ソフトウェアに依存していました。

var openFolderDialog = new OpenFolderDialog()
{
    Title = "Select folder to open ...",
    InitialDirectory = Environment.GetFolderPath(
        Environment.SpecialFolder.ProgramFiles)
};

string folderName = "";
if (openFolderDialog.ShowDialog())
{
    folderName = openFolderDialog.FolderName;
}

詳細については、「.NET 8 での WPF ファイル ダイアログの機能強化 (.NET ブログ)」を参照してください。

NuGet

.NET 8 以降、NuGet は Linux 上の署名付きパッケージを既定で検証します。 NuGet は引き続き Windows でも署名済みパッケージを検証します。

ほとんどのユーザーは、この検証に気付くことはありません。 ただし、既存のルート証明書バンドルが /etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem にある場合は、警告 NU3042 と共に信頼エラーが発生する可能性があります。

環境変数 DOTNET_NUGET_SIGNATURE_VERIFICATIONfalse に設定することで、検証をオプトアウトできます。

診断

C# ホット リロード ではジェネリックの変更がサポートされます

.NET 8 以降、C# ホット リロード ではジェネリック型とジェネリック メソッドの変更がサポートされています。 Visual Studio を使用してコンソール、デスクトップ、モバイル、または WebAssembly アプリケーションをデバッグするときに、C# コードまたは Razor ページのジェネリック クラスとジェネリック メソッドに変更を適用できます。 詳細については、Roslyn でサポートされている編集の完全なリストを参照してください

関連項目

.NET ブログ