個人的なメモ

Tomohiro Suzuki @hiro128_777 のブログです。Microsoft MVP for Developer Technologies 2017- 本ブログと所属組織の公式見解は関係ございません。

.NET 6 新機能のまとめ(随時更新中)

2021年11月9日から11月11日にかけて、.NET Conf 2021.が開催され、NET 6 もとうとうローンチされました。Preview1 から GA まで段階的に情報や新機能がリリースされており、情報も分散しているため、まとめて確認できる目次がなく不便なので、情報を整理してみました。
  
公式情報も更新されているため、記事は随時更新中です。
なお、各情報の(.NET 6 RC 2)の「.NET 6 RC 2」は、その情報に対する公式の解説があったバージョンを表しています。
  

  

.NETプラットフォームの統合(.NET 6 Preview 4)

.NETプラットフォームの統合は、遡ること2年半、2019年5月6日に Microsoft の公式ブログ「Introducing .NET 5」の中で、「.NET - A unified Platform」としてコンセプトが発表され、2020年11月の .NET 5 のローンチで統合が完了する予定でした。
 
.NET - A unified Platform のコンセプトは以下のとおりです。
・1つのSDK、1つのランタイム、1つのコードベースで、クラウド、ウェブ、デスクトップ、モバイル、ゲーム、IoT、AI、あらゆる種類のアプリケーションを構築できる
・.NETを学んだ後は、同じスキルで上記のすべてのワークロードを構築できるようになる
 
ですが、残念ながら 2020年の .NET 5 では、Xamarin の一部である Android、iOS、および macOS の 機能の統合は間に合わず、.NET 6 での統合と延期されました。今回、.NET 6 では、.NET 5 で先送りになった、Xamarin の一部である Android、iOS、および macOS の 機能の統合が完了する予定でしたが、残念ながら Xamarin の統合はまた延期となりました。ただし、今回は Preview として利用可能(※1)になっており、完全な統合まであと少しとなっています。(それまでは、従来の Xamarin が更新され続け、本番用のモバイルアプリの構築には Xamarin が推奨となります。)
 
具体的には、.NET MAUI(※2) のローンチが 2022年第2四半期の初めを目標に延期されたことにより、.NET 6 の iOS、Android、macOS、MacCatalyst の各ワークロードの GA も 2022年第2四半期の初めへ延期となっています。
 
あと半年程度で、ついに .NET におけるプラットフォームの統合が完了し、.NET - A unified Platform が実現します。
 
なお、Preview ではすでに、Xamarin.Android、Xamarin.iOS の名称ではなく、Microsoft.iOS、Microsoft.Android が使用されています。

f:id:hiro128:20211114163012p:plain f:id:hiro128:20211114162929p:plain

 
(※1)Preview として、Windows 版の Visual Studio 2022 から .NET MAUI が利用可能です。(Visual Studio 2022 for Mac はまだ未対応)
Visual Studio 2022 for Mac でも ios、android-aot のワークロード(Microsoft.iOS、Microsoft.Android)が Preview で利用できます
 
(※2).NET MAUI は、.NET Multi-platform App UI の略称です。Xamarin.Formsの後継の Android、iOS、macOS、および Windows 用の .NET を使用してネイティブクロスプラットフォームアプリを構築するためのマルチプラットフォームアプリ UI です。
 
公式情報:
devblogs.microsoft.com
docs.microsoft.com
 
 

サポート

.NET 6 は2021年11月8日にリリースされ、LTSリリースとして3年間サポートされます(2024年11月8日まで)。

Version リリース日 リリースタイプ サポート期限
.NET 6 2021/11/8 LTS 2024/11/8
.NET 5 2020/11/10 Current 2022/5/8
.NET Core 3.1 2019/12/3 LTS 2022/12/3

 
.NET 6 は .NET Framework と .NET Core が統合されてから最初の LTS リリースです。.NET Framework からの移行をどうすべきかについても気になることでしょう。(移行すべきかどうかの考え方については後述します)
 
サポートに関する詳細は以下をご覧ください。
github.com
  

対応プラットフォーム

動作プラットフォームは以下のとおりです。

Windows x86, x64, Arm64(クライアントのみ)
Linux x64, Arm32, Arm64
Android x64, Arm32, Arm64
iOS x64(シミュレータ), Arm32, Arm64
Mac x64, Arm64

  
詳細は以下をご覧ください。
github.com
 
 

.NET 6 のターゲットフレームワークモニカ(TFM)

新しいプラットフォームのサポートの追加で、TFM が追加されています。TFM は csproj ファイルの TargetFramework セクションに記載します。TFM とは「ターゲットフレームワークの呼称」という意味で、アプリまたはライブラリで使用できるようにする API のセットを表すものです。

以下のように .csproj ファイル内で定義します。

<TargetFramework>net6.0</TargetFramework>


.NET 6 にはOS固有のバインディングを含む以下の TFM があります。.NET Framework 時代には、TFM から利用できる API が直感的にわからず非常に難解でしたが、ベースの TFM (net6.0)と OS 固有のフレーバー(windows、Android、ios など)によって構成されるよう整理され、とてもわかりやすくなりました。

  • net6.0
  • net6.0-android
  • net6.0-ios
  • net6.0-maccatalyst
  • net6.0-macos
  • net6.0-tvos
  • net6.0-windows

devblogs.microsoft.com
  

SDKワークロード(.NET 6 RC 1)

TFMが、主要な機能とオプションの組み合わせで表現されることに合わせて、SDK 自体も主要な機能と各ワークロードのオプションに分割されています。.NET 6 では新たに追加された、.NET MAUI、Android、iOS、WebAssembly が別のワークロードとして用意されています。これは、これまでのような、すべての機能を含むモノリシック SDK では、ビルド時間が長くなり、配布サイズも大きくなってしまうからです。
  
詳細は以下をご覧ください。
devblogs.microsoft.com

  
dotnet workload list コマンドを実行すると、以下のようにインストールされているすべてのワークロードが一覧表示されます。
docs.microsoft.com
 

Installed Workload Ids
----------------------
android-aot
ios
maccatalyst
macos
maui
tvos
wasm-tools

 
.NET 7 では、ASP.NET や Windows デスクトップなどのコンポーネントもオプションになる予定となっています。
 
 

.NET 6 のハイライト

.NET 6 のハイライトは、基本的に以下の 4 つに集約されます。

  1. パフォーマンスの向上
  2. 開発の生産性の向上
  3. セキュリティ対策の向上
  4. macOS、Arm64関連 の対応

 

パフォーマンスの向上

.NET 6 ではランタイムに対する多数のパフォーマンス改善が行われています。ライブラリ開発者の方にとっては詳細を理解することがとても有効ですが、アプリ開発者の場合、.NET6 をターゲットフレームワークにすれば自動的にパフォーマンスが改善するものもあるため、移行するだけでもある程度のメリットを享受できます。
 
公開されているベンチマークで Node.js よりも10倍速い、また、Entity Framework Core のパフォーマンスは .NET 5 と比べて最大92%の速度向上を実現しているなどのパフォーマンス向上を実現しており、既存の .NET Web アプリケーションを .NET 6 にマイグレーションすることで速度の向上を見込めます。
 
.NET でパフォーマンスの向上に注力する最大の理由は、「クラウドコンピューティングのコスト削減」です。少ないコンピューティングリソースで必要なパフォーマンスを実現できることで、クラウドのコストを最適化できます。
 
以下、具体的なパフォーマンスを改善の例のいくつかを簡単にご紹介します。
 

JIT(ジャストインタイムコンパイラ)の改善によるインライン化の最適化

「インライン化による最適化」は、コンパイラがメソッドの呼び出し先からコードを取得し、それを呼び出し元に直接出力するプロセスです。これにより、メソッド呼び出しのオーバーヘッドが回避されます。さらに呼び出し先のコードが結果として定数を返すような場合、コンパイル時に計算を完了させてしまい、呼び出し元のコンテキストに展開することで、メソッド呼び出しを操作全体を定数に変換できます。

ただし、インライン化を行うとすべての呼び出し元に呼び出し先の結果のコードがコピーされるため、内容的には同じコードだとしても、CPUから見れば別のコードと扱われるため、CPUのキャッシュメモリ上で多くのエビクション(キャッシュメモリからの削除)とロードが強制されてしまい、かえって速度が低下してしまう場合もあります。それを最適化するために、コンパイラがインライン化するかどうかの判断を厳密に行うように改善し、パフォーマンスを向上させています。

公式情報:Performance Improvements in .NET 6
devblogs.microsoft.com
  

IOの改善

FileStream の実装はとても古くパフォーマンスに関連する多くの問題がありましたが、ほとんど手付かずのままでした。.NET 6 では FileStream がほぼ完全に書き直されており、速度と信頼性が高まりました。また、複数のバッファーに対応した(Scatter / Gather IO)新しい API が導入されています。Scatter / Gather IO を使用すると、単一の sys-call で複数のバッファーを渡すことにより、高コストの sys-call の数を減らすことができます。
 
Scatter / Gather IO は「ベクトル化 I/O」とも呼ばれ、単一の sys-call が複数のバッファからデータを順次読み込んで単一のデータストリームに書き込む、またはデータストリームからデータを読み込んでバッファのベクトルで定義された複数のバッファに書き込む入出力方式です。
 
関連記事 
hiro128.hatenablog.jp
 
公式情報:
devblogs.microsoft.com
 
 

ディクショナリーの値が構造体の場合の処理の高速化(.NET 6 Preview 3)

新しい unsafeな API - CollectionsMarshal.GetValueRefOrNullRef - が追加され、ディクショナリー内の構造体の値をより速く更新できるようになりました。
なお、この新しいAPIは、一般的なアプリ開発の用途ではなく、ゲーム開発やライブラリ開発など高性能なシナリオを想定しています。

このAPIは、構造体の値への参照を返しその場で更新することができます。これまでは、構造体の値の更新には、辞書の検索と構造体のスタックへのコピーが必要で、高性能なシナリオではコストの問題がありました。今回の改良では、キーのハッシュ化が2回から1回に減り、構造体のコピー操作がすべて不要になりました。

なお、GetValueRefOrNullRef を利用する場合、Unsafe.IsNullRef(ref value) を使用して null 参照の確認が必要です。

この API 追加のバックグラウンドをご説明します。
変更可能な構造体を利用する場合、誤って構造体をコピーしてしまい、元の構造体とコピー先の構造体の値が異なるものになってしまうトラブルが発生しがちです。

これを防ぐには「構造体をコピーした上で値を変更し結果を元の場所にコピーして戻す」必要があります。これは小さな構造体なら問題ありませんが、構造体が大きい場合、高コストになり問題です。そもそもパフォーマンス向上のために変更可能な構造体を使用する場合が多いので、いったんコピーし変更の上、再度元の場所にコピーし直すというのは本末転倒です。

なお、NET 5 以降なら、List<T> に対しては、CollectionsMarshal.AsSpanメソッドでコレクションの Span ビュー(ref 構造体)を取得できます。

同じようにディクショナリーでも ref 構造体を取得できるようにするため、CollectionMarshal.GetValueRefOrNullRef という新しい API が提供されました。なお、実はこの機能は internal メソッドDictionary<TKey, TValue>.FindValue として既に存在しており、.NET 6 で GetValueRefOrNullRef メソッドとして公開されました。

github.com
公式情報:
Faster handling of structs as Dictionary values
CollectionsMarshal.GetValueRefOrNullRef<TKey,TValue>(Dictionary<TKey,TValue>, TKey) メソッド
 
 

開発の生産性の向上

開発の生産性の向上のために .NET インナーループのパフォーマンス向上が行われています。「インナーループ」とは、コードの変更、ビルド、テストなどで繰り返し使用するプロセスのことです。
 
インナーループの一部として頻繁に使用するツールやワークフローを最適化することで、ビルド時間の短縮、デバッグ時の手順の簡略化、実行中アプリの変更のライブ反映(Hot Reload)など、開発中の細かい待ち時間が短縮され、コーディングの時間が増えることで生産性を最大化できます。
 
なお、Hot Reload は Windows Form アプリ、WPF アプリ、Web アプリ、MAUI アプリ(予定)に対応しています。
 
インナーループの改善では、定常状態のパフォーマンスではなく、ランタイム、アプリモデル、dotnet CLI、MSBuild などの起動時のパフォーマンスや、ツールのエンドツーエンドのパフォーマンスに焦点を当てています。最適化したいアプリケーションの関連領域をプロファイリングし、得られたデータを分析して影響が大きい原因やボトルネックを探し、それに対する修正策を考え、さらに次の影響力のある項目を見つけるためにプロセスを繰り返して改善が進められています。
結果として new、build、run などの dotnet コマンドを中心に、開発者のインナーループに関わる主要な部分のオーバーヘッドを削減されています。
 

  • これまでの改善例は以下のとおりです
    • ツールが予期せずJIT化していた箇所の修正(dotnet/installer #9635)、
    • ロギングが有効になっていなくても高コストなロギング関連の作業が発生するのを回避(dotnet/aspnetcore #27956)、
    • MSBuildにおけるグロブ(ワイルドカードでファイル名のセットを指定するパターン)の最適化(dotnet/msbuild #6151)、
    • ASP.NET Razor コンパイラを Roslyn ソースジェネレータを使用するように変更して余分なコンパイルプロセスを回避 (dotnet/sdk #15756)
    • .NET ホストを変更してマシン上のアンチウイルスツールをトリガする可能性の低いファイルアクセスパターンを使用するように改善 (dotnet/runtime #48774)

 
公式情報:Theme — Improve .NET Inner Loop Performance
 
 

ランタイムのセキュリティ脆弱性の緩和策(.NET 6 RC 1)

ランタイムのセキュリティ脆弱性の緩和策については以下の記事を参照ください。
hiro128.hatenablog.jp 
 

macOS Arm64 および Windows Arm64 に関するアップデート

Windows や .NET では最近 Arm64 関連のサポートが強化されていますが、これは見逃せない動きです。Arm64 ブラットフォームは x64 プラットフォームに比べ電力効率で大幅に優れています。 クライアントマシン 1 台レベルでは、消費電力の差はたかが知れていますが、クラウドのデータセンターの規模になるとその差は膨大になります。Arm64 のサポートが今後進めば、クラウドでも Arm64 の利用がどんどん進むことでしょう。x86 と併用される場合でも、同じ性能であれば Arm64 の方が料金が安くなる可能性が高いと予想しています。将来を見据え、場合によっては x86 から Arm64 に移行できる準備も進めておくためにも、Arm64 の動向はウオッチしておいた方が良いでしょう。
 

SDK / ランタイム関連(.NET 6 RC 2)

.NET 6 RC2 以降 では、x64 および Arm64 .NET インストーラーがサイドバイサイドでインストールされるようになり(x64 と Arm64 のビルドが別々の場所にインストールされます)x64 と Arm64 の共存を実現しています。(これまでは、x64 と Arm64 のビルドがお互いに上書きされてしまっていました)

 
macOS や Windows の Arm64 マシンで、.NET 6 RC2 以降を利用するには、Arm64 と x64 の .NET 6 と 以前の .NET(.NET 5 / .NET Core 3.1 など)のすべての .NET SDKとランタイムを一旦アンインストールして再インストールをする必要があります。なお、再インストール時に利用する以前のバージョンの .NET(.NET 5 / .NET Core 3.1 など)の .NET SDKとランタイムは今後提供する予定です。
 
SDKは、Arm64 では .NET 6 以降のみがサポートされます。それ以前のSDKによるビルドはArm64ではブロックされます。ランタイムは、Arm64 および x64 のすべてのサポート中のバージョンがサポートされます。

.NET 6 RC2 以降では、(x64エミュレーションを含む)Arm64 向けの最終的な .NET 6 エクスペリエンスの大部分が提供されています。
 
Windows Arm64 用の .NET 5 SDK は、.NET 6 RTM に合わせて早期にサポートが終了されます。
 
Arm64 アーキテクチャ上では、ネイティブ・アーキテクチャのパフォーマンスの享受、1つの SDK を管理すればよいなど、よりより良い体験ができるため Arm64 SDKの利用を推奨されています。
 
なお、.NET Framework でも、「.NET Framework 4.8.1」で Arm64 のサポートが追加されます。


公式情報:macOS and Windows Arm64 Update
 

macOS ユニバーサルバイナリー(.NET 6 Preview 1)
  • .NET 6 では、ワークフローで .NET アプリのユニバーサルバイナリーを公開することはできない
  • この機能の必要性を .NET 7 で再検討する。
  • 現在、macOS 向けとしては Intel 専用のバイナリーしかビルドきない。ユニバーサルバイナリーではない Apple Silicon 専用のバイナリーもビルドできない。
  • ビルドした Intel 専用のバイナリーを Rosetta 2 を介して Apple M1 上で動作させることはできる。

公式情報:Native Apple Silicon
 
 

.NET 6 に移行すべきか .NET Framework 4.8 にとどまるべきか

状況が許すのであれば、 .NET 6 に移行することを推奨します。.NET 6 で移行を見送る場合でも、次の LTS である 2023 年の .NET 8 リリースをターゲットに移行を進めるように進めることをお勧めします。
移行にあたり、.NET アップグレード アシスタントを利用すると、SDKスタイルの .csproj への移行、TFM(TFMについては後述します)のアップデート、NuGet パッケージの更新、テンプレートファイルや config ファイルの追加などの定型的な移行についてのガイドが表示され、ステップ・バイ・ステップで自動的に移行を進めることができるので、移行作業の負荷が下がります。ただし、移行を完全に完了するためにさらに手作業での修正が必要です。
 
なお、.NET アップグレード アシスタントで以下の種類の .NET Framework アプリの移行がサポートされています。

  • .NET Framework Windows フォーム アプリ
  • .NET Framework WPF アプリ
  • .NET Framework ASP.NET MVC アプリ
  • .NET Framework コンソール アプリ
  • .NET Framework クラス ライブラリ

 
そして、.NET Framework アプリを .NET 6+ への移行をお勧めする最大の理由はセキュリティです。
 

パッチではない新たなセキュリティ対策は .NET 6 以降にのみ実装される

上記、セキュリティ対策のセクションで述べたように、すでに .NET 6 にセキュリティ対策関連や HTTP/3 など新たな注目すべき機能が実装され始めており、 2年後の .NET 8(次のLTS) リリース時にはかなりの機能の差が拡大すると見込まれます。そして、これらの新機能は .NET Framework 4.8 では提供されません。

特にセキュリティ対策関連については、今後のロードマップが示されていますが、これらのセキュリティ対策は単なるセキュリティパッチではなく、CPU の新機能やランタイムの構造に手を入れる根本的な対策を含んでおり、それらは .NET 6 以降にのみ実装されます。
したがって、.NET 8 の世代では、 .NET Framework 4.8 はまだサポート期間内なので動作はするものの、セキュリティ対策の観点からはあくまでもパッチの提供にとどまり、新機能による根本的な対策は行われず、世代的に陳腐化したものとなってしまうと考えられます。
 
これらを加味した、移行の指針を以下に示します。
  

新規アプリなら .NET 6 を選択

いまだに、.NET 6 を「枯れていない」ということから避ける意見も見受けられますが、.NET Core の系統も、.NET 6 で 5 世代目であり充分「枯れて」います。もはや新規アプリ開発のリスクは .NET Framework 4.8 と差はないと考えて問題ありません。このタイミングで .NET 6 を「枯れていない」として避けることは、逆に「ではいつになれば、枯れていると判断できるのか」という理由付けが必要な状況になってしまうでしょう。
 

既存アプリもなるべく.NET 6 に移行

問題になるとしたら、既存アプリ移行のシナリオでサードパーティー製のライブラリが .NET 6 に対応していない、開発元がすでに存在しないというようなケースです。この場合、代替のライブラリを探したり、同等機能をスクラッチ開発しなければならないなどリスクはあります。ですが、サードパーティー製の .NET Framework 対応のライブラリの保守も継続はされるでしょうが、もはやあくまでも延命措置としての保守であり、今後サポートの優先度を下げられてしまうリスクも考慮すると、.NET 6 へ移行するのが賢明です。
 

.NET Framework 4.8 のまま継続した方が良いケース

例えば、当該アプリを動かす環境が一切パブリックネットワークに接続しておらず完全に閉域で使用されるアプリであれば、そのまま .セキュリティ面のリスクも少ないため、ギリギリまで .NET Framework 4.8 のまま維持しそのまま終息させてしまう選択は有効です。
 
 
よって、かなり限定されたケース以外では超えなければいけないハードルはあるにせよ、このタイミングで移行を開始し2年後の .NET 8 リリース時には移行完了するようなスケジュールを検討するのはとても良い選択と考えられます。
 
 

.NET 6 への移行計画の作成

.NET 6 への移行計画の作成に便利なツールについては以下の記事を参照ください。
hiro128.hatenablog.jp
  
 

API の追加

FileStreamを使用しない、ファイルの読み取りと書き込み

.NET 6には、FileStreamを使用せずにファイルの読み取りと書き込みを可能にする新しい低レベルAPIが追加されています。
devblogs.microsoft.com

 

C# 10 の更新情報

C# 10 の更新情報については以下の記事を参照ください。
hiro128.hatenablog.jp

 

C#プロジェクトテンプレートのモダナイズ(.NET 6 RC 2)

新しいテンプレートでは、以下のような言語機能が使われています。
これらの機能に共通するテーマは、コードエディタでコードを見るときのノイズ(例えば、お作法的な記述)を減らし、より重要な側面を表すシグナルを増やすことです。
公式情報:
.NET SDK: C# project templates modernized
破壊的変更: テンプレートの C# コードが以前のバージョンでサポートされない
 

 

コンソールアプリテンプレート(.NET 6 RC 2)

コンソールアプリのテンプレートは、.NET 5 のものと比べはるかに最小限ですが、機能の低下はありません。

Program.cs はたったこれだけになります。
using System; も namespace も Program クラスも Main メソッドも記述しません。

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

また、暗黙的な using ディレクティブがオプトイン機能となり(ImplicitUsings)、テンプレートで有効になっています。

csproj ファイル

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

公式情報:Console template
 

Web アプリテンプレート(.NET 6 RC 2)

  • Web アプリのテンプレートも同様に最小限のものとなった。
  • Program.cs のみが必要で、Startup.cs が不要になった。

公式情報:Web templates
 

Windows Forms アプリテンプレート

  • 1行の構文のファイルスコープ付き名前空間宣言を使用するようになった
  • 暗黙的な using ディレクティブが有効になった

公式情報:Windows Forms templates
 
 

ライブラリー:ネイティブメモリのAPI(.NET 6 Preview 7)

  • System.Runtime.InteropServices.NativeMemory.NativeMemory を介して公開される新しいネイティブメモリー割り当てAPIが追加された
  • アプリケーション開発者向けではない
  • ライブラリ開発者がパフォーマンスをチューニングしたい時などに利用する想定

API の詳細:NativeMemory クラス

公式情報:
Libraries: NativeMemory APIs

 
 

ASP.NET Core のアップデート

.NET 6 の ASP.NET Core のアップデートについては以下の記事を参照ください。
hiro128.hatenablog.jp
 
 

.NET MAUI のアップデート

.NET MAUI のアップデートについては以下の記事を参照ください。
hiro128.hatenablog.jp
 
 
以下、随時更新中です。