個人的なメモ

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

Windows も ARM へ移行が進む?ARM64 にネイティブ対応する Visual Studio 2022

ARM64 ネイティブ対応の背景

Microsoft は Visual Studio 2022 で 64ビット化対応を完了しました。そして次のステップとして Visual Studio の ARM64 ネイティブ対応を急速に進めています。2022年9月7日現在、ARM64 Visual Studio 2022 はプレビュー版がリリースされており、年末までに GA される予定となっています。
 
ではなぜここにきて Microsoft は急速に Visual Studio 2022 は ARM64 ネイティブ対応を進めているのでしょうか。今回はその背景と現時点での進捗について確認していきます。また、実際に ARM64 Visual Studio 2022 のプレビュー版に触れてみた感触についてもレポートいたします。
 
 

背景その1 持続可能性の価値の高まり

MicrosoftがVisual Studio の ARM64 ネイティブ対応を進める理由の一つは、.NET アプリを ARM64ネイティブで運用できる環境を整えたい動機が高まっているからです。その大きな理由のひとつは「Sustainability(持続可能性)」への取り組みです。
 
ARM 64アーキテクチャはx64に比べ電力効率に優れており、コストだけでなく持続可能性に大きく寄与するポテンシャルを持っています。昨今、持続可能性への取り組みは企業価値を高める指標として重要な位置を占めており、アプリは単にユーザビリティとパフォーマンスが優れているだけではテクノロジーによる社会への貢献というアプリのミッションを果たすことはできなくなりつつあります。
 
また、アプリの開発ではこれまでQCDが重視されていましたが、今後は運用時のコストとエネルギーの消費にもいっそうの配慮が必要になるため、その有効なソリューションとしてARMアーキテクチャへの注目が高まっています。このような背景においては.NETの開発者としても既存アプリのマイグレーションや新規アプリ開発時に持続可能性の観点も含めARM 64アーキテクチャ採用の知見を蓄積していく必要があります。
 
特にスケールを前提としたサーバーコンピューティングではエネルギー削減効果が顕著であり、サービス提供側が関与できないクライアントコンピューティングと違い ARM64 の導入が進みやすい状況にあります。
 
なお、Azure ではすでに ARM64 対応の VM が2022年9月1日にGAされており、以下の画像のように East US リージョンなどですでに利用可能です。なお、ARM64 対応の VM は x64 の VM に比べ概ね20%程度安価になっています。

 
docs.microsoft.com
 
 

背景その2 エコシシテムの整備

Microsoft は2022年中にAI関連の開発者向けの NPU 搭載 ARM アーキテクチャ Windows PC である Project Volterra を投入予定です。NPU は Neural Processing Unitの頭文字で、ニューラルネットワークや機械学習の高速化を専門に処理するプロセッサです。過去に CPU とは別にグラフィックを専門に処理するGPUが搭載されていったように、昨今の AI 需要の高まりによって、NPU を CPU や GPU とは別に搭載するケースが出てきました。将来的には過去の GPU がそうだったようにほとんどのデバイスに NPU が搭載されることになるだろうと言われています。
 
www.youtube.com
 
例えば iPhone では 2017年発売の iPhone 8、iPhone X 世代の Apple A11 Bionic プロセッサから標準で NPU が内蔵されています。
 
Microsoftはこれまでに何度かWindows のARM対応を進めてきましたが、Windows単体のリリースにとどまり、積極的な訴求活動も行われなかったため本格的な普及はしませんでした。しかし、今回はこれまで述べたようにWindows 11 ARM 64だけでなく、開発環境を整備するためのProject Volterra やVisual Studio、Visual Studio Code、デプロイ環境である Azure やLinuxといったようなエコシステム全体への積極的な投資を進めています。また、Microsoftは既存アプリをARMプロセッサ搭載デバイスに移植するための支援も計画しています。
 
この流れは、Apple M1 以降のMacのARM64化の辺りから顕著になってきており、M1 Macの影響は大きかったと考えられます。
 
 

ARM64 Visual Studio 2022のインストール

ARM64 Visual Studio 2022 は、Windows 11 ARM 64で利用できます。
 
注意点としてARM64 Visual Studio 2022 をインストールするには、いったんインストール済みのVisual Studio 2022を全てアンインストールし、Visual Studio 2022 の最新のプレビューをインストールします。
 
visualstudio.microsoft.com
 
Windows 11 ARM 64の環境を手軽に準備するには、AzureのWindows 11 ARM 64 VMを利用するか、M1/M2搭載の Mac に Parallels を導入し Windows 11 のライセンスを用意するのが最も簡単です。(この記事では M1 Mac の環境で検証を行なっています。)
 
また、.NET 6 は既定で ARM64 ネイティブ対応ですが、 .NET Framework についてもバージョン4.8.1 をインストールすることで ARM64 ネイティブ対応となります。
 
PowerShell でインストールされている .NET Framework のバージョンを調べることができます。
 

Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse | Get-ItemProperty -Name version -EA 0 | Where { $_.PSChildName -Match '.*L.*'} | Where { $_.Version -gt 4.8} | Select PSChildName, Version

 
以下のように「4.8.09037」以上が確認できれば、.NET Framework 4.8.1 がインストールされています。インストールされていなければ以下よりダウンロードできます。
 

PSChildName Version
----------- -------
Client      4.8.09037
Full        4.8.09037 

 
dotnet.microsoft.com
 
現在、ARM64で利用できるワークロードは以下の画像の通り「ASP.NETとWeb開発」「.NETデスクトップ開発」「C++によるデスクトップ開発」「ユニバーサルWindowsプラットフォーム開発」の4つです。今後、.NET MAUIなどの他のワークロードもサポートされる予定です。

 
x64版と同様に利用したいワークロードにチェックを入れて「ダウンドードしながらインストールする」を実行すればインストールされます。
 
 

ARM64 Visual Studio 2022の使用感とアプリのx64版からの移行


ARM64 Visual Studio 2022 のルックアンドフィールは x64版と違いはありません。違和感なくx64版と同じ感覚で使用できます。

 
違いはバージョン情報にARM 64版である表記が入るくらいです。

 
また、アプリの移行についても.NET の世界に閉じている場合、非常にスムーズです。
※アンマネージドのdllなどをラップしている場合には、ARM64に対応したdllを準備する必要があります。
 
実際に、x64 Visual Studio 2022で作成した簡単な.アプリを、コードを何も変更せずARM64 Visual Studio 2022でビルド、デバッグ実行した様子を以下でご紹介します。
 
 

.NET Framework アプリのARM64への移行

x64 Visual Studio 2022で作成した簡単な.NET Framework のアプリを、コードを何も変更せずARM64 Visual Studio 2022でビルドしてみました。簡単なアプリのため少ない機能しか使用されていませんが、ビルド完了までは非常にスムーズで問題なく成功しx64版となんら違いは見られませんでした。
 
注意点として既定のAny CPU のビルド構成でビルドするとDebug でもReleaseでもなぜかx86でビルドされ、エミュレーションでの実行となってしまいます。
 
構成マネージャー

 
実行アプリのアーキテクチャ



回避するには以下の画像のように明示的にビルド構成でARM64を構成した上でビルドすることで、ARM64ネイティブでビルド、実行されます。

構成マネージャー

 
実行アプリのアーキテクチャ

 
 

.NET6 アプリのARM64への移行

同様にx64 Visual Studio 2022で作成した簡単な .NET6 のアプリを、コードを何も変更せずARM64 Visual Studio 2022でビルドしてみました。こちらも簡単なアプリのため少ない機能しか使用されていませんが、こちらもビルド完了までは非常にスムーズで問題なく成功しx64版となんら違いは見られませんでした。
 
また、.NET6の場合は既定のAny CPU のビルド構成でビルドしても期待通りARM64ネイティブでビルド、実行されます。
 
構成マネージャー

 
実行アプリのアーキテクチャ

 
 

最後に

ここまで見たようにARM64 Visual Studio 2022はプレビュー版とはいえすでにかなりの完成度に達しており、ワークロード側の対応が進めば迅速にリリースされることでしょう。.NET6はすでにWindows、Linux、MacOS全てのプラットフォームでARM64対応のランタイムがリリース済みです。
 
クライアントPCがARMアーキテクチャーに置き換わっていくにはもう少し時間がかかりそうですが、サーバーコンピューティングはクラウドベンダーであるMicrosoftが自社の企業価値を高めるためにも、Azureでサービスを提供する企業が自社の企業価値を高めるためにも今後急速にARM対応は進んでいくと予想されます。
 
.NETの開発者としてはいち早く自社のアプリのARM対応について検証を進め、おそらく我々の予想より早く到来するであろう.NETアプリの多数がARMで運用される時代の到来に備えることをお勧めいたします。
 
なお、今後の.NETの予定ですが、11月に .NET 7 のリリースが控えています。11月8日から10日(現地時間)には、ローンチイベント「.NET Conf 2022」も開催予定ですので、今後の情報公開が楽しみです。

.NET 7 の注目機能となるか? Orleans

Orleans に関しては、さらに詳細な仕様や、本記事のローカル実行ではなく、Azure App Service にデプロイしてみた結果について以下の記事にまとめました。
 
hiro128.hatenablog.jp
 
 

.NET 7 の動向と Orleans

.NET 7 の新しいトピックとして Orleans がバージョン4となって.NET 7と共にリリースされることが発表されています。Orleans は日本ではこれまであまり注目されていなかったフレームワークですが、2010年頃から開発は続いており現時点でバージョン3.6がリリースされている息の長いフレームワークです。
 
devblogs.microsoft.com
 
上記ブログにて、「ASP.NET Core チームと Orleans チームは、Orleans 分散プログラミング モデルを ASP.NET Core とさらに調整および統合する方法を調査しています。Orleans 4 は .NET 7 と共に出荷されます。」と発表されており、docs.microsoft.com で Orleans のドキュメントが公開されたことからも .NET 7 以降 Orleans を積極的に推していく意思が感じ取れます。
 
docs.microsoft.com
 
そこで今回は、 .NET 7 における注目機能の一つになるかもしれない Orleans をご紹介します。
 
 

Orleans とは

公式ドキュメントの説明では「Orleans は、堅牢でスケーラブルな分散アプリケーションを構築するためのクロスプラットフォームフレームワークです。」とありますが、この説明では具体的に Orleans を使用することでどんなメリットがあるのかよくわかりません。

最も単純な例として、モノリシックのアプリケーションをクラウドにデプロイしスケールアウトするシナリオを考えてみましょう。何らかの工夫をしない限りアプリケーションの複数の実行単位(たとえば、App service のインスタンスや k8s の Pod)間はそれぞれお互いの実行内容を知ることはできないため、キャッシュの共有やステートの管理の考慮、HTTP リクエストごとにデータベースへのアクセスが発生することによるデータベースへの負荷、データ更新処理の競合などの問題が発生します。

このような状態を回避するために、ステート管理、処理のキューイング、共有キャッシュの制御やデータ更新処理の整合性制御などの機能が必要となります。

これらの機能はモノリシックのアプリケーションのスケール時に限らず分散アプリケーションでは必須ですが、自前で実装するのは車輪の再発明になってしまいます。また、設計やテストに非常に手間がかかり、速度や品質を担保するのも困難です。このような厄介な問題の面倒を見てくれるフレームワークが Orleans です。

Orleans を利用することによって、シングルサーバーアプリケーションの開発経験しかない開発者でも、比較的容易に回復性とスケーラビリティを備えたクラウドネイティブアプリケーションや分散アプリケーションの開発に移行できることが最大のメリットとなります。
 
 

グレインとは

グレインは「粒」という意味を持つ Orleans アプリケーションの基本的な構成要素で、状態データを持つコンピューテーションの単位です。 グレインは他のグレインとメッセージを送受信したり、クライアントからの要求に対するレスポンスを返します。

グレインはユーザー定義のID(identity)、動作(behavior)、および状態(state)で構成され、プライベートまたは共有の揮発的または永続的な状態データを保持可能で、様々なストレージシステムに格納することができます。

グレインによってアプリケーションの状態を暗黙的に分割し自動的な拡張性を実現するとともに、障害からの復旧が容易になります。グレインの状態は、グレインがアクティブな間はインメモリに保持されるため、レイテンシーが低く保たれデータストアの負荷も低くなります。
 
 

Orleans のメリット

Orleans を利用すると開発者が意識することなく分散アプリケーションに必要な機能上のメリットを享受できるため、開発者の専門性の高低を問わず生産性が向上します。こちらも公式ドキュメントの説明は抽象的でわかりにくいため、主なメリットについてもう少しわかりやすく以下でご説明します。
 

グレインのシングルスレッド実行の保証

グレインのシングルスレッド実行が保証されるため、開発者はグレインレベルでの同時実行に直面することがなく、共有データへのアクセスを制御するためにロックや他の同期メカニズムを使用する必要がなくなり、分散プリケーション開発時の考慮事項が少なくなります。
 

グレインのアクティブ化と非アクティブ化について開発者は意識不要

アプリケーションは、論理的に作成されたグレインの「メモリ空間」全体に、それらが特定のタイミングで物理メモリ上に存在するかどうかにかかわらず中断することなくアクセスすることができます。個々のグレインは Orleans のランタイムによって必要に応じてページイン・ページアウトされるため、グレインの物理メモリへのロード、エビクション、活性化、非活性化といったライフサイクルを開発者がアプリケーションコードによって管理する必要はなく、Orleans のランタイムによってハードウェアリソースに応じて動的にグレインのロードバランシングが最適化されます。
 

グレインの物理的な場所に関して開発者は意識不要

グレインの参照(プロキシオブジェクト)は、グレインの論理的なアイデンティティのみを含んでいます。アプリケーションコードはグレインの物理的な位置を意識することなくグレインと通信できます。グレインの物理的な位置は、障害やリソース管理、または呼び出された時点で当該のグレインが非アクティブになっているなどの理由で、時間の経過とともに変化する可能性がありますが、それを開発者がアプリケーションコードによって管理する必要はなく、意識する必要もありません。
 

永続的なストアとの統合について開発者は意識不要

Orleans は、Grainのインメモリ状態を宣言的に永続ストアにマッピングすることが可能です。また、開発者が意識することなく呼び出し側が永続的な状態が正常に更新された後にのみ結果を受け取ることが保証されます。永続ストアの選択肢の自由度は高くカスタマイズも可能です。
 

エラーの自動伝播

Orleans では、開発者はエラー処理ロジックを適切な場所に配置すればよく、処理されなかったエラーはランタイムによって自動的にコールチェーン上に伝播されます。エラーがアプリケーション内で握りつぶされることはなく、各レイヤーでエラーを手動で伝播させるという面倒な作業を行う必要はありません。
 

開発者が意識することなくスケーラビリティと高パフォーマンスを実現できるプログラミングモデル

Orleansのプログラミングモデルは実績のあるベストプラクティスとパターンを取り入れているため、アプリケーションやサービスを拡張する際に、開発者が自然にコードを記述すれば適切なコードになるように設計されています。
 
 

Orleans の HelloWorld のサンプルコード

それでは、Orleans の HelloWorld サンプルを見ていきましょう。記事の都合上さわりの説明だけに留まりますが、公式の HelloWorld サンプルに手を加え、Web API のエンドポイントを追加しグレインの活性化、非活性化を操作し可視化できるように変更しています。
 
なお、このサンプルコードは説明のために調整されたものですので、ベストプラクティスではありませんのでご注意ください。
 

Web APIのエンドポイント

Web API のエンドポイントのリファレンスは以下の通りです。

エンドポイント メソッド レスポンス
/ Get "Orleans is running." 文字列を固定で返却。この時 Grain が非活性であれば自動で活性化される。
/datetime Get 現在の日時を返却。この時 Grain が非活性であれば自動で活性化される。
/deactivate Get "HelloGrain is deactivated." を固定で返却する。その後 Grain はアイドル状態になると即時非活性となる。

 
 

ソリューションの構成

ソリューションの構成は以下の通りです。Grain の活性化、非活性化が確認できる最低限の実装です。

 
 

コード

それぞれのコードと意図は以下の通りです。
 

Program.cs
  • Orleans ホスト設定・起動して待機状態にする
  • Web API を起動して待機状態にする
using System;
using HelloWorld;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Orleans;
using Orleans.Hosting;

// Orleans ホスト設定・起動
using var orleansHost = new HostBuilder()
    .UseOrleans(builder => builder.UseLocalhostClustering())
    .Build();
await orleansHost.StartAsync();

// グレインファクトリー取得
var grainFactory = orleansHost.Services.GetRequiredService<IGrainFactory>();

// HelloGrain の参照を"friend"キーで取得
var friend = grainFactory.GetGrain<IHelloGrain>("friend");

// Web API 起動
var app = WebApplication.Create();
app.MapGet("/", async () => await friend.SayHello("Orleans is running."));
app.MapGet("/datetime", async () => await friend.SayHello($"{DateTime.Now}"));
app.MapGet("/deactivate", async () => await friend.SayHelloDeactivateOnIdle("HelloGrain is deactivated."));
await app.RunAsync();

// Orleans ホスト終了
await orleansHost.StopAsync();

 

IHelloGrain.cs
  • 文字列キーを持つ Grain のインターフェース
  • メソッドを定義する
using System.Threading.Tasks;
using Orleans;

namespace HelloWorld
{
    public interface IHelloGrain : IGrainWithStringKey
    {
        Task<string> SayHello(string greeting);
        Task<string> SayHelloDeactivateOnIdle(string greeting);
    }
}

 

HelloGrain.cs
  • Grain の実装クラス
  • 活性化、非活性化のステータス遷移時にその旨をコンソールに出力する
using System;
using System.Threading.Tasks;
using Orleans;

namespace HelloWorld
{
    public class HelloGrain : Grain, IHelloGrain
    {
        public Task<string> SayHello(string greeting)
        {
            return Task.FromResult($"Hello, {greeting}");
        }

        public Task<string> SayHelloDeactivateOnIdle(string greeting)
        {
            DeactivateOnIdle();
            return SayHello(greeting);
        }

        public override Task OnActivateAsync()
        {
            Console.WriteLine($"{DateTime.Now} Grain status : HelloGrain({IdentityString}) is activating...");
            return base.OnActivateAsync();
        }

        public override Task OnDeactivateAsync()
        {
            Console.WriteLine($"{DateTime.Now} Grain status : HelloGrain({IdentityString}) is deactivating...");
            return base.OnDeactivateAsync();
        }
    }
}

 
 

動作の説明

では、このコードを実際に実行して以下の4つのステップの基本的な動作について確認していきましょう。

  1. アプリケーション起動
  2. API の動作確認
  3. Grain を非活性に遷移させる
  4. アクセス検知により Grain を再度活性に遷移させる

 

1. アプリケーション起動

サンプルアプリケーションを起動すると、Grain が活性化状態になります。
 
コンソールの出力

2022/08/21 17:55:46 Grain status : HelloGrain(*grn/HelloWorld.HelloGrain/0+friend-0x6EDED8DA) is activating...

https://localhost:5001/ にアクセスすると、固定文字列のレスポンスが返却されます。

 

2. API の動作確認

https://localhost:5001/datetime にアクセスすると、現在の日時のレスポンスが返却されます。

 

3. Grain を非活性に遷移させる

https://localhost:5001/deactivate にアクセスすると、固定文字列のレスポンスが返却されます。

 
レスポンス返却後アイドル状態となるため Grain が非活性に状態遷移します。
 
コンソールの出力

2022/08/21 17:56:10 Grain status : HelloGrain(*grn/HelloWorld.HelloGrain/0+friend-0x6EDED8DA) is deactivating...

 

4. アクセス検知により Grain を再度活性に遷移させる

再度、https://localhost:5001/datetime にアクセスすると、まず Grain が非活性から活性に状態遷移します。
 
コンソールの出力

2022/08/21 17:56:26 Grain status : HelloGrain(*grn/HelloWorld.HelloGrain/0+friend-0x6EDED8DA) is activating…

Grain が活性化されたのち、現在の日時のレスポンスが返却されます。

 
 
以上の通り、開発者が Grain の状態遷移を意識することなく単純にロジックを実装するだけで、Orleans のランタイムによって状態遷移が管理されていることがわかります。
 
 

まとめ

Orleans の最も基本的な動作を見ていきましたが、とても興味深いフレームワークであることがわかります。今回のサンプルはローカルで動作する HelloWorld レベルですが、公式ドキュメントには Azure の App Service にショッピング カートアプリをデプロイするようなさらに実践的な情報もありますので興味を持たれた方はぜひそちらもご覧ください。
 
docs.microsoft.com


繰り返しになりますが、Orleans 4 は .NET 7 と共に出荷され、今後 Orleans 分散プログラミング モデルは ASP.NET Core に統合されていくとのことですので、11月の .NET 7 のリリースが楽しみです。11月8日から10日(現地時間)には、ローンチイベント「.NET Conf 2022」も開催予定ですので、今後の情報公開が楽しみです。



 

.NET MAUI、 Xamarn.Forms からの改良ポイント

.NET MAUI リリース

2022年5月23日(現地時間)ついに .NET MAUI が GAされました。Microsoft は「.NET MAUI は、.NET Multi-platform App UI の略称で、モバイル、タブレット、デスクトップにまたがるネイティブデバイスのアプリケーションを構築するためのフレームワークです。」と紹介しています。
 
2022年6月26日現在では、残念なことに .NET MAUI 自体は GA されたのですが、開発環境である Visual Studio は Windows 版も Mac 版も最新のプレビュー版での対応となっています。こちらは、ほどなく安定版の Visual Studio でも対応されるでしょう。
 
.NET MAUI は Xamarin.Forms から全く違う製品名に変わりましたが、何が違うのでしょうか。わかりやすく言えば、.NET MAUI は Xamarin.Forms の改良版です。では、具体的に .NET MAUI は Xamarin.Forms と比べどこが改良されたのか詳しく見ていきましょう。
 
 

Xamarin.Forms と .NET MAUIの主な違い

まず、Xamarin.Forms と .NET MAUI の主な違いをまとめてみました。色々違いがありますが、.NET 6 対応になりさまざまな点で改良されています。

Xamarin.Forms .NET MAUI
プロジェクトの構造 非 SDK スタイル(Franken-proj)
プラットフォームごとに個別のプロジェクトを使用
SDK スタイル
単一のプロジェクトで、複数プラットフォームをターゲットにできる
ツールチェーン .NETFramework .NET CLI
リソース管理 プラットフォームごとに個別管理
プラットフォーム固有のデバイスの解像度に応じたイメージを準備する必要がある
単一のプロジェクト内で一元管理
SVG を準備すれば各プラットフォームの解像度のPNGに変換でされる
スタートアップ 独自(App クラス) Generic Host 対応
ホットリロード 完全な XAML ホットリロード
(SDK5.x および Visual Studio 2019 16.9以降)
完全な .NET ホットリロード
完全な XAML ホットリロード
UI コントロールアーキテクチャ レンダラー ハンドラー

 
次に、.NET MAUI の改良点の中で特に注目したいポイントをご紹介します。
 
 

.NET MAUI のプロジェクト構成

.NET MAUI のプロジェクトは以下のようになっています。Xamarin.Forms のような「単一の共有プロジェクト + 複数のプラットフォーム固有プロジェクト」の構成ではなく、単一のプロジェクトで、複数プラットフォームをターゲットにできるようになりました。
 

 
 

Xamarin.Forms のアーキテクチャーに起因する問題点

Xamarin.Forms は登場当時、画期的なフレームワークでした。ですが、利用が進むにつれてアーキテクチャーに起因する問題点も顕在化していきました。
 

Xamarin.Forms のレンダラーアーキテクチャー

以下の図のようにレンダラーは共有プロジェクトの UI コントロール実装に依存しています。
 

 
 

Xamarin.Forms レンダラーアーキテクチャーの問題点

  • レンダラーが Xamarin.Forms の UI コンポーネントと密結合している。
  • アセンブリスキャンとリフレクションというコストの大きい処理を使用して UI コントロールのレンダラーを取得するため遅い。
  • 共有プロジェクトからネイティブ UI コントロールをカスタマイズする場合、たった1つのプロパティの変更であっても、共有プロジェクトとネイティブプロジェクトにまたがるレンダリングの仕組みを理解した上で、定型的な多くのコードを記述する必要があり、非常に手間がかかる。

 
 

.NET MAUI の最大の変更点

Xamarin.Forms からの最大の変更点は、レンダラーアーキテクチャーからハンドラーアーキテクチャーへの変更です。ハンドラーアーキテクチャの採用によってプラットフォーム固有のレンダリングの責務を、抽象化 UI フレームワークの実装から分離しました。
 

.NET MAUI で導入されたハンドラーアーキテクチャー

以下の図のようにハンドラーはインターフェースにのみ依存し、抽象化 UI コントロールの実装には依存しません。
また、共有コード内で複数のプラットフォームのネイティブ UI コントロールを直接操作できます。複数プラットフォーム対応のために条件付きコンパイルを使用しなければならないのは美しくないですが、残念ながらこれより良い手段は現在のところありません。

 

 
これによって以下に述べるようなアドバンテージが生まれました。
 

.NET MAUI ハンドラーアーキテクチャーのアドバンテージ

  • ハンドラーはアセンブリスキャンが不要となり速度が向上した。
  • 共有コード内でもでもネイティブ UI コントロールに直接アクセスしてプロパティを変更できる。
  • ハンドラーは、スタートアップコードの Generic Host 内に直接記述も可能で、簡単に実装および使用できるため、特定のコントロールまたはアプリ全体で使用されるすべてのコントロールのカスタマイズが簡単に実装できる。

 

(参考)Generic Host

Generic Host は、アプリの起動やシャットダウンのようなライフタイム管理やアプリの構成やロギング、DIのようなアプリのビジネスロジック自体とは関係ない基盤となる機能をカプセル化するオブジェクトです。これによって、アプリの基盤に関わる機能とビジネスロジックを分離できクリーンな構造になります。
 
Generic Host の詳細はこちらをご覧ください。
docs.microsoft.com
 
 

.NET MAUI のスタートアップコードの例

.NET MAUI では Generic Host に対応したため、スタートアップの処理が Xamarin.Forms とは大きく異なっています。
では、.NET MAUI のスタートアップコードのサンプルを見ていきましょう。
 
なお、以下コードは説明のために調整されたものですので、ベストプラクティスではありませんのでご注意ください。
 
まずは、コード全体を見ていきましょう。
Generic Host 内では、

  • 起動するアプリクラスの指定
  • フォントの登録
  • Dependency injection
  • ハンドラーを利用した UI のカスタマイズ

を行なっています。
 
スタートアップコードはMauiProgram.csに記述します。

 
MauiProgram.cs

using MauiUICustomizeSample.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;

namespace MauiUICustomizeSample;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder .UseMauiApp<App>()  // 起動するアプリクラスの指定
            .ConfigureFonts(fonts => // リソースフォルダ内のフォントの登録
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        // Dependency injection : AppSlell クラスを DI コンテナに登録
        builder.Services.AddTransient<AppShell>();

        // 全ての Label のカスタマイズ
        LabelHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
        {
            if (view is Label)
            {
#if IOS
                handler.PlatformView.BackgroundColor = Colors.MediumSpringGreen.ToPlatform();
#elif ANDROID        
                handler.PlatformView.SetBackgroundColor(Colors.MediumSpringGreen.ToPlatform());
#endif
            }
        });

        // 特定のインスタンスの Button のカスタマイズ
        ButtonHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
        {
            if (view is MyButton)
            {
#if IOS
                handler.PlatformView.BackgroundColor = Colors.LightCoral.ToPlatform();
                handler.PlatformView.SetTitleColor(Colors.White.ToPlatform(), UIKit.UIControlState.Normal);
                handler.PlatformView.Layer.CornerRadius = 7;
#elif ANDROID
                handler.PlatformView.SetBackgroundColor(Colors.LightCoral.ToPlatform());
#endif
            }
        });

        return builder.Build();
    }
}

 
 
次に個別の処理について詳しく見ていきます。
 

MAUI 用の Generic Host のビルダーを作成

MauiAppCreateBuilderメソッドで MauiAppBuilder のインスタンスが作成されます。

var builder = MauiApp.CreateBuilder();

 
 

起動するアプリクラス(このサンプルでは App クラス)を指定

MauiAppBuilderUseMauiAppメソッドの型パラメーターに起動するアプリクラスの型(App)を指定します。

builder.UseMauiApp<App>()

 
App クラスはApp.xaml.csで定義されています。

 
App.xaml.cs のコードは以下のようになっています。

namespace MauiUICustomizeSample;

public partial class App : Application
{
	public App(AppShell appShell)
	{
		InitializeComponent();
		MainPage = appShell;
	}
}

 
 

フォントの登録

プロジェクト内の /Resources/Fonts 内に配置されたフォントを登録します。

 
MauiAppBuilderConfigureFontsメソッド内のデリゲートで登録を行います。

.ConfigureFonts(fonts =>
{
    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});

 
 

AppSlell クラスを DI コンテナに登録

AppSlell クラスを DI コンテナに登録し、インスタンスが注入されるようにします。

builder.Services.AddTransient<AppShell>();

 
これによって、App.xaml.cs のコンストラクタの appShell パラメータにインスタンスが注入されます。

 
App.xaml.cs

namespace MauiUICustomizeSample;

public partial class App : Application
{
	public App(AppShell appShell)
	{
		InitializeComponent();
		MainPage = appShell;
	}
}

 
 

ハンドラーを利用した UI のカスタマイズ

サンプルでは、「特定の種類の UI コントロール全てをカスタマイズする場合」と「ある UI コントロールの特定のインスタンスのみをカスタマイズする場合」の2つのパターンの UI カスタマイズを行なっています。両者とも Xamarin.Forms の場合と比較して驚くほど簡単にカスタマイズができるようになっています。
 

特定の種類の UI コントロール全てをカスタマイズする場合

こちらでは、Label コントロール全ての背景色をMediumSpringGreenに変更します。
 

 
MauiProgram.cs

        // 全ての Label のカスタマイズ
        LabelHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
        {
            if (view is Label)
            {
#if IOS
                handler.PlatformView.BackgroundColor = Colors.MediumSpringGreen.ToPlatform();
#elif ANDROID        
                handler.PlatformView.SetBackgroundColor(Colors.MediumSpringGreen.ToPlatform());
#endif
            }
        });

AppendToMappingActionデリゲート内で、UI コントロールの型が Labelの場合に、カスタマイズしたい色を設定します。

handler.PlatformViewには、iOS の場合 UILabel、Android の場合 AppCompatTextViewが割り当てられていますので、それぞれのネイティブ UI コントロールのプロパティを変更することで色を変更できます。設定する色はToPlatformメソッドによってそれぞれのプラットフォームのネイティブカラーに変換されます。
 
ここで注目したいことは、たった数行のコードで共有コード内のUILabel.BackgroundColorや、AppCompatTextView.SetBackgroundColorといったプロパティやメソッドが使用できていることです。

Xamarin.Forms でこのようなことをするには、各プラットフォームプロジェクト内でクラスを定義し定型的なコードを書く必要がありましたので、比較にならないほどシンプルになっているのがわかります。
 

ある UI コントロールの特定のインスタンスのみをカスタマイズする場合

こちらでは、Button コントロール特定のインスタンスの背景色をLightCoralに変更します。
 
Button のサブクラス MyButton を作成します。

MyButton.cs

namespace MauiUICustomizeSample.Controls
{
	public class MyButton : Button
	{
		public MyButton()
		{
		}
	}
}

 
MauiProgram.csMyButton に対してカスタマイズを適用します。

 
MauiProgram.cs

        // 特定のインスタンスの Button のカスタマイズ
        ButtonHandler.Mapper.AppendToMapping(nameof(IView.Background), (handler, view) =>
        {
            if (view is MyButton)
            {
#if IOS
                handler.PlatformView.BackgroundColor = Colors.LightCoral.ToPlatform();
                handler.PlatformView.SetTitleColor(Colors.White.ToPlatform(), UIKit.UIControlState.Normal);
                handler.PlatformView.Layer.CornerRadius = 7;
#elif ANDROID
                handler.PlatformView.SetBackgroundColor(Colors.LightCoral.ToPlatform());
#endif
            }
        });

AppendToMappingActionデリゲート内で、UI コントロールの型が MyButtonの場合に、カスタマイズしたい色を設定します。
 
handler.PlatformViewには、iOS の場合 UIButton、Android の場合 MaterialButtonが割り当てられていますので、それぞれのネイティブ UI コントロールのプロパティを変更することで色を変更できます。設定する色はToPlatformメソッドによってそれぞれのプラットフォームのネイティブカラーに変換されます。
 
カスタマイズしたコントロールを XAML 内で使用するには、xmlns:button="clr-namespace:MauiUICustomizeSample.Controls" のように名前空間を指定します。
 
MyButtonのみ背景色がカスタマイズされます。
 

 
MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:button="clr-namespace:MauiUICustomizeSample.Controls" 
             x:Class="MauiUICustomizeSample.MainPage">
			 
    <ScrollView>
        <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0" 
            VerticalOptions="Center">

            <Image
                Source="dotnet_bot.png"
                SemanticProperties.Description="Cute dot net bot waving hi to you!"
                HeightRequest="200"
                HorizontalOptions="Center" />
                
            <Label 
                Text="Hello, MAUI!"
                SemanticProperties.HeadingLevel="Level1"
                FontSize="32"
                HorizontalOptions="Center" />
            
            <Label 
                Text="Welcome to .NET Multi-platform App UI"
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="Welcome to dot net Multi platform App UI"
                FontSize="16"
                HorizontalOptions="Center" />

            <HorizontalStackLayout 
                Spacing="10"  
                HorizontalOptions="Center">
                
                <Button 
                    x:Name="CounterBtn"
                    WidthRequest="120"
                    Text="Click me"
                    SemanticProperties.Hint="Counts the number of times you click"
                    Clicked="OnCounterClicked"
                    HorizontalOptions="Start" />

                <button:MyButton
                    x:Name="CustomizedBtn"
                    WidthRequest="120"
                    Text="Customized"
                    HorizontalOptions="End" />

            </HorizontalStackLayout>
        </VerticalStackLayout>
    </ScrollView>
 
</ContentPage>

 
iOS での実行結果は以下のようになります。
ラベルの全体の背景色と、右側のボタンの背景色がカスタマイズされています。
 

 
ハンドラーを使用したコントロールのカスタマイズの詳細はこちらをご覧ください。
docs.microsoft.com
 
 

まとめ

これまでご説明しましたように、.NET MAUI では、Xamarin.Forms の面倒だった部分が非常に扱いやすくなり、直感的でシンプルなコードで記述できるよう改善されました。また、アセンブリスキャンやリフレクションなどコストの高い処理を回避することで起動速度も向上しています。
 
つまり「 .NET MAUI = よりシンプルで使いやすくなった Xamarin.Forms」と言えます。というわけで、早速使ってみましょう!



 
 

.NET 6 で開発できるアプリケーションの整理

はじめに

.NET Framework が2002年にリリースされてから20年という歳月が流れ、当初はWindows 専用でコンソールアプリやデスクトップアプリ、Webアプリのみの対応であったものが、.NET 6では、WindowsやLinuxやMac、iOSやAndroidなど様々なプラットフォーム上で動作するアプリやWebアプリ、IoT、ゲームなど様々なアプリ開発できるようになりました。さらに、たとえばWebアプリを開発する場合でも複数のテクノロジーが利用可能となっています。
その反面、あまりにできることが多すぎて、アプリを開発するときに自分のシナリオにあったテクノロジーを選択するための全貌を把握するのが困難になっています。
 
今回は新規アプリを開発するシナリオでも.NET Frameworkから.NET 6へ移行するシナリオでも、どのワークロードを利用するのがよいか迷わないように開発できるワークロードの種類と特長について概要を整理します。
 
逆にワークロードごとの詳細な情報は公式ドキュメントも充実しており検索で多数でヒットしますので、そちらを参照していただけますと幸いです。
 
 

ワークロードごとの主要なアプリテンプレート対応一覧

ほとんどの.NET Frameworkのワークロードには対応した.NET 6のワークロードが準備されていますが、一部対応する.NET 6のワークロードがないものがあります。また.NET5や.NET 6で新規に登場したワークロードもあります。
 

移行前(.NET Framework / mono) 推奨移行先(.NET 6)
アプリの種類 ランタイム/SDK プラットフォーム アプリの種類 ランタイム/SDK プラットフォーム
コンソール アプリ コンソール .NET Framework Windows コンソール .NET 6 Windows/Linux/macOS
Windows アプリ Windows フォーム .NET Framework Windows Windows フォーム .NET 6 Windows
WPF .NET Framework Windows WPF .NET 6 Windows
UWP Universal Windows Windows WinUI Windows App SDK Windows
Web アプリ ASP.NET Web Forms .NET Framework Windows 移行先なし
ASP.NET MVC .NET Framework Windows ASP.NET Core MVC .NET 6 Windows/Linux/macOS
Blazor Server .NET 6 Windows/Linux/macOS
Blazor WebAssembly .NET 6 Windows/Linux/macOS
モバイルアプリ iOS Xamarin.iOS iOS iOS .NET 6 iOS
Android Xamarin.Mac Android Android .NET 6 Android
Xamarin.Forms Xamarin.Forms Android/iOS/Windows(UWP) .NET MAUI .NET MAUI .NET 6 Android/iOS/macOS/Mac Catalyst/Windows(WinUI)
macOS アプリ Mac Xamarin.Mac macOS Mac Catalyst .NET 6 macOS/Mac Catalyst
Azure Azure Functions .NET Framework Azure Functions (Runtime 1.x) Azure Functions .NET 6 Azure Functions (Runtime 4.x)
Azure WebJobs .NET Framework Azure WebJobs Azure WebJobs .NET 6 Azure WebJobs

 
では、ワークロードごとの特長について、それぞれ解説していきます。
なお、以下で移行についても述べますが、ここでの「移行」とは、あくまでも.NET Frameworkアプリのソースコードを.NET 6でビルドし動作できるようにする最低限度の移行作業を意味しています。アプリケーションのモダナイゼーションを本格的に進めるような場合は、さらに追加の作業が必要となるのでご留意ください。
 
 

コンソールアプリ

・.NET Upgrade Assistant対応

コンソールアプリは.NET6でも引き続きサポートされています。「最上位レベルのステートメント」「暗黙的なusingディレクティブ」「グローバルusingディレクティブ」といった新機能によって.NET Framework時代より非常に簡潔にアプリケーションコードが記述できるようになっています。
もちろん、以前と同じスタイルでアプリケーションコードを記述することも可能です。.NET Upgrade Assistantに対応しており、.NET Frameworkアプリからの移行に関する手間も少なく、比較的容易に移行が可能です。
.NET Framework時代はWindows専用でしたが、.NET6ではWindows / Linux / macOSでの利用が可能になりました。
 
 

Windowsフォームアプリ

・.NET Upgrade Assistant対応

Windowsフォームアプリはすでに過去のものというイメージがあるかもしれませんが.NET6世代でも新規コントロールの追加新機能の追加など手厚くサポートされておりまだまだ現役で利用可能です。
Windowsフォームの利点は何といっても初心者の開発者でも素早くアプリを開発できることなので、WPFやWinUI 3の難易度が高いと感じた場合や小規模のアプリであれば新規アプリに使用しても全く問題ありません。

MVVMのようなUIとロジックの分離についても公式のライブラリがないので多少手間がかかりますが、検索すれば多くの情報がヒットしますのでそれを参考にCommandやViewModelのベースを自前実装してしまえば分離可能です。

.NET FrameworkのWindowsフォームアプリからの移行先としても、.NET6のWindowsフォームアプリを第一の選択とすることをお勧めします。.NET Upgrade Assistantに対応しているため移行の一部も自動化できます。

.NET FrameworkのWindowsフォームアプリを.NET6のWPFに移行したいような場合は、結局大幅な改修となってしまうので移行ではなく新規開発としたほうがよいでしょう。
 
 

WPFアプリ

・.NET Upgrade Assistant対応

Windows デスクトップアプリを新規で開発するシナリオの場合、WPFかWinUI 3 にするかという選択になります。公式の推奨はWinUI 3なのですが、UWP系のGUIライブラリは短期間に方針が変わり迷走しているのでWinUI 3が今後も安定してサポートされ続け利用できるかということに少し疑問があります。(新たなライブラリが発表されWinUI 3が更新されなくなってしまう懸念)

逆にWPFは今後も長期間にわたってサポートされ続けると予想されますので、PC上でマウス操作するアプリを開発するならWPFが無難と言えます。(WPFはタッチ操作が一般的になる前にリリースされたライブラリのためタッチ操作にフレンドリーな設計になっていません。

タブレットでタッチ操作前提のアプリであればWinUI 3の方がお勧めとなります。
.NET FrameworkのWPFアプリの移行先としては.NET6のWPFアプリが推奨です。
 
 

UWPアプリ

これからUniversal Windows Platformアプリを新規で開発するならUWPアプリではなくWinUI 3アプリが推奨となります。
UWPはPC以外のWindowsデバイスでの動作も視野に入れていましたが、Windows Phoneが終了し、Surface Hubもあまり普及していませんのでUWPを新規で採用する理由はほぼありません。
マイクロソフトもUWPが実質的に互換性維持のために提供される状態へ入ったと意図する説明をしています。
既存アプリのアップグレードをする場合は、WinUI 3への移行となります。
 
 

WinUI 3アプリ

WinUI 3は2022年現在、開発に採用する場合においては少し慎重な判断が必要な立ち位置となっています。

WinUI 3はWindowsデスクトップアプリとUniversal Windows Platformアプリを開発可能です。
公式の推奨はWinUI 3なのですが、UWP系のGUIライブラリは短期間に方針が変わり迷走しているのでWinUI 3が今後も長期間安定して利用できるかということに疑問があります。ユーザーに受け入れられず、さらに新たなライブラリが発表され更新されなくなってしまう懸念があるので、ユーザーに受け入れられてしっかり普及するかどうか、もうしばらく様子見でもよいと考えられます。
また、WinUI 3はUWPをベースにしているため、使い勝手がWPFとは違う部分があるので、WPFに慣れておりWPFで事足りる場合はWPFを採用する方が無難です。
 
 

ASP.NET Web Forms アプリ

ASP.NET Web Formsは.NET Frameworkでのみ開発できます。.NET6に後継はありません。よって、現在、新規のWebアプリを開発するシナリオでASP.NET Web Formsを選択することはありません。

ASP.NET Web Formsアプリを.NET6に移行する場合、移行先としてはBlazor Server アプリが案内されていますが、実際には「移行」という範囲を超えたコードの書換えが必要になります。あくまでもBlazor Server アプリの設計思想がこれまでASP.NET Web Formsの開発者に理解しやすいという意味での案内となっています。
ASP.NET Web Forms は今後も.NET Framework 4.8のサポートが終了するまでサポートされますので、可能な限り現行のアプリを延命することが第一の選択になります。
どうしてもASP.NET Web Formsアプリを.NET6に移行したい場合、ASP.NET Core MVCアプリ、Blazor Serverアプリ、Blazor WebAssembly アプリのどれかに移行することになります。
 

ASP.NET MVC アプリ / ASP.NET Core MVC アプリ

・.NET Upgrade Assistant対応

ASP.NET Core MVC アプリを新規で開発するシナリオの場合、.NET6でもASP.NET Core MVCアプリとしてサポートされているので問題なく開発可能です。ただし、新規アプリを開発する場合には最初にサーバー上でロジックを実行しWebページのレンダリング結果をクライアントに送信する従来型のWebアプリかブラウザーでUIロジックの大部分を実行し、Web APIを介してサーバーと通信するシングルページアプリケーション (SPA) を選択する必要があります。
そのうえで、従来型のWebアプリであればASP.NET Core MVCをSPAであればBlazor ServerアプリまたはBlazor WebAssemblyを選択します。

Webアプリのテクノロジーの選択については以下の記事も参考になります。

参考情報:従来の Web アプリケーションかシングル ページ アプリケーション (SPA) を選択する
docs.microsoft.com


移行の場合であれば、.NET Upgrade Assistantに対応していますので、移行プロセスの一部は自動で実行可能です。なお、ASP.NET Core MVCでは起動プロセスの取り扱いとJavaScriptやCSSの配置方法が変更されているので書き換えが必要となります。
 
 

iOS アプリ

.NET でiOSアプリを開発するシナリオとして最も有効なのは、Windows アプリを iOS に移植したい場合でiOSらしいUXを実現したいというケースです。UIは再作成が必要ですが、ロジック部分はかなりの部分が流用可能です。

iOS アプリを新規で開発する場合、.NET6でもサポートされているので問題なく開発可能です。
2022年5月8日時点で.NET MAUIはRC2までリリースされていますので、近いうちにほとんど現状のままで正式版がリリースされると考えられます。
Visual Studio 上からはiOS アプリのテンプレートが選択できませんが、以下のようにコマンドでプロジェクトを新規作成できます。

dotnet new ios

移行の場合も、SDKの内容の変更はほとんどありませんので、.csprojファイルをSDKスタイルに書換えTFMを「net6.0-ios」に設定し、Visual Studio上のエラーや警告を修正すれば移行終了です。
今後正式版がリリースされた時点で何らかの自動アップグレードの手段が提供される可能性はあります。
 
 

Android アプリ

.NET でAndroidアプリを開発するシナリオとして最も有効なのは、Windows アプリを Androidに移植したい場合でAndroidらしいUXを実現したいというケースです。UIは再作成が必要ですが、ロジック部分はかなりの部分が流用可能です。

Androidアプリを新規で開発する場合、.NET6でもサポートされているので問題なく開発可能です。
2022年5月8日時点で.NET MAUIはRC2までリリースされていますので、近いうちにほとんど現状のままでGAされると考えられます。
Visual Studio 上からはAndroidアプリのテンプレートが選択できませんが、以下のようにコマンドでプロジェクトを新規作成できます。

dotnet new android

移行の場合も、SDKの内容の変更はほとんどありませんので、.csprojファイルをSDKスタイルに書換えTFMを「net6.0- android」に設定し、Visual Studio上のエラーや警告を修正すれば移行終了です。
今後正式版がリリースされた時点で何らかの自動アップグレードの手段が提供される可能性はあります。
 
 

Xamarin.Forms アプリ

Xamarin.Formsアプリを開発するシナリオとしては、Windows アプリを iOSとAndroidに移植したい場合でiOSやAndroidらしいUXにはそこまでこだわりがない場合があてはまります。UIは再作成が必要ですが、ロジック部分はかなりの部分が流用可能となります。
また、あまり複雑でないiOS/Androidのクロスプラットフォームアプリを開発したいが、開発者が.NETに慣れておりiOS/Androidには不慣れなケースもあてはまります。不慣れなことにチャレンジするよりはストレスなく始めることが可能となります。

なお、これからXamarin.Forms アプリを新規で開発する場合注意が必要です。
Xamarin.Formsは.NET MAUI の正式版がリリースされてから 1年後(2023年6月頃と予想されます)にサポートが終了するため、開発するアプリのリリース時期に応じて、一旦Xamarin.Forms で開発し.NET MAUIへ移行するか、今の時点で.NET MAUIのRC2を使って開発を進めてしまうか決めることになります。
 
 

.NET MAUI アプリ

.NET MAUIアプリを開発するシナリオとしては、(Xamarin.Formsと同じですが)Windows アプリを iOSとAndroidに移植したい場合でiOSやAndroidらしいUXにはそこまでこだわりがない場合があてはまります。UIは再作成が必要ですが、ロジック部分はかなりの部分が流用可能となります。
また、あまり複雑でないiOS/Androidのクロスプラットフォームアプリを開発したいが、開発者が.NETに慣れておりiOS/Androidには不慣れなケースもあてはまります。不慣れなことにチャレンジするよりはストレスなく始めることが可能となります。

.NET MAUIの正式版がリリースされた時点(2022年6月までにはリリースされる予定)からは.NET MAUI アプリを選択することが推奨になります。
 
 

Xamarin.Mac アプリ / Mac Catalyst アプリ

.NET でMacアプリを開発するシナリオとして最も有効なのは、Windows アプリを macOS に移植したいというケースです。UIやプラットフォームに依存のコードは再作成が必要ですが、プラットフォームに関係ないビジネスロジックのコードは流用可能です。

Macアプリを新規で開発する場合、.NET6でもサポートされているので問題なく開発可能です。
2022年5月8日時点で.NET MAUIはRC2までリリースされていますので、近いうちにほとんど現状のままで正式版がリリースされると考えられます。
Visual Studio 上からはMacアプリのテンプレートが選択できませんが、以下のようにコマンドでプロジェクトを新規作成できます。

dotnet new maccatalyst

移行の場合も、SDKの内容の変更はほとんどありませんので、.csprojファイルをSDKスタイルに書換えTFMを「net6.0-maccatalyst」に設定し、Visual Studio上のエラーや警告を修正すれば移行終了です。
今後正式版がリリースされた時点で何らかのXamarin.Macからの自動アップグレードの手段が提供される可能性はあります。

Blazor Serverアプリ/ Blazor WebAssembly アプリ

.NETでSPAを開発したいシナリオの場合Blazor Serverアプリを検討します。Blazorの場合Vueなどを利用する場合と比べてJavaScriptへの深い理解が不要となるため、.NETに慣れておりJavaScriptの経験が浅い開発者がSPAを開発するシナリオでは有効な選択肢となります。

Blazor Serverアプリでは アプリのロジックはC#で記述しサーバー上で実行されます。 クライアントのブラウザー側はプレーンなHTML とJavaScriptとなり、 UI の操作が行われることでロジックの動作や画面の更新などが必要になった場合はサーバーとSignalR 通信が行われ、返ってきたレスポンスでブラウザーのUIが更新されます。
この動作モデルがASP.NET WebForm と似ているため、公式ではASP.NET WebFormの移行先としてBlazor Serverアプリを推奨しています。

.NETでBlazor ServerアプリよりもさらにリッチなUIを提供したいシナリオや、サーバーとの通信が不要なWebアプリを提供したいシナリオではBlazor WebAssemblyアプリを検討します。(もちろん、Blazor WebAssemblyアプリでもサーバーとの通信は利用できます。)

Blazor WebAssemblyアプリはその名の通り、C#でWebAssemblyを開発できます。WebAssemblyはブラウザー上で動作する非常に高度なUIをJavaScriptに不慣れな開発者でもC#で記述することができます。コンセプトとしてはSilverlightに似ていますが、プラグインを使用ないことやオープンな Web 標準を使用している点がSilverlightより優れています。

Blazor ServerアプリとBlazor WebAssembly アプリのどちらを選択するかについては以下の記事も参考になります。

参考情報:どの Blazor ホスティング モデルを選択するか
docs.microsoft.com
 
 

Azure Functions

Azure Functionsの関数アプリを新規開発したい場合、.NET6での開発が推奨となります。
すでに稼働中の関数アプリを.NET6に移行したい場合は検証が必要ですが、関数アプリの性質上、互換性が問題となる影響があることはそれほど多くなく、発見された場合でも容易に修正が可能な場合が多いでしょう。

アプリケーションコードの移行については以下の記事も参考になります。

参考情報:既存のアプリのアップグレード
docs.microsoft.com
 
 

Azure WebJobs

・.NET Upgrade Assistant対応

WebJobsはApp Service上で定期的なプログラムを実行するシナリオやApp ServiceにデプロイされたWebアプリやAPIアプリ内からプログラムを実行させるシナリオで利用します。

WebJobsを新規開発したい場合、.NET6での開発が推奨となります。
.NET6の場合WebJobsのプロジェクトテンプレートが用意されていないので、コンソールアプリを作成し必要に応じてAzure WebJobs SDKインストールします。詳しい手順については以下の記事が参考になります。

参考情報:チュートリアル: イベント ドリブンのバックグラウンド処理に Azure WebJobs SDK の使用を開始する
docs.microsoft.com



.NET FrameworkのWebJobsからの移行先としては、要件によってはAzure Functionsに移行することも選択肢の一つとなります。

Functionsに移行すべきか、WebJobsのまま移行すべきかについては以下の記事も参考になります。

参考情報:Functions と WebJobs の比較
docs.microsoft.com
 
.NET6のWebJobsに移行する場合は.NET Upgrade Assistantに対応しているため移行の一部も自動化できます。
 
 

まとめ

.NET6はどんなプラットフォームで動くどんなアプリでも開発できるすばらしい開発環境になった反面、自分のシナリオに合ったワークロードの見極めが少々難しくなっています。

業務アプリケーションを開発する際に利用が想定されるワークロードについては一通り概要に触れることができました。
 
 

.NET MAUI Preview 7 の更新情報

.NET MAUI Preview 7 の更新情報についてご紹介します。

なお、オリジナルの記事はこちらです。
devblogs.microsoft.com
 
.NET MAUI Preview 8 の更新情報はこちらです。
hiro128.hatenablog.jp
 
 

新しいレイアウト(.NET MAUI Preview 7)

新しいレイアウトはデフォルトで有効になっており、新しいレイアウトは 7 年間の Xamarin.Forms のレイアウトから得た知見を採用した新しい LayoutManager アプローチに基づいて、一貫性、パフォーマンス、メンテナンス性が最適化されています。
 
従来の Xamarin.Forms のレイアウトは、Xamarin.Formsからの移行プロジェクトとの互換性のために Microsoft.Maui.Controls.Compatibility 名前空間にのみ存在するようになりました。
 
公式情報:New Layouts
 
 

アクセシビリティの変更と改善(.NET MAUI Preview 7)

TabIndex および IsTabStop の削除(.NET MAUI Preview 7)

TabIndex と IsTabStopプロパティは、開発者がスクリーンリーダーで読まれるUI要素の順序を制御するために Xamarin.Forms で導入されたが、実際には混乱を招きニーズを満たしていませんでした。
 
.NET MAUI では、インターフェイスの構造をプログラムで操作するのではなく、読まれたいように UI を順序付ける設計アプローチを推奨しています。
.NET MAUI で順番をコントロールしなければならない場合には、コミュニティツールキットの SemanticOrderView を推奨しています。
 
公式情報:TabIndex and IsTabStop Removed
 

SetSemanticFocus とアナウンス(.NET MAUI Preview 7)

新しい SemanticExtensions クラスの一部として、スクリーンリーダーのフォーカスを特定の要素に移動させることができるように、新しい SetSemanticFocus メソッドが追加されました。
 
公式情報:SetSemanticFocus and Announce
 

フォントスケーリング(.NET MAUI Preview 7)

すべてのプラットフォームのすべてのコントロールで、フォントスケーリングがデフォルトで有効になりました。
 
これにより、OS 上でテキストスケーリングの設定を調整すると、その設定がアプリケーションの UI に反映され、デフォルトでよりアクセシブルなアプリケーションが実現できます。
 
公式情報:Font Scaling
 
 

その他の注目すべき変更と追加(.NET MAUI Preview 7)

公式情報:Additional Highlights

  • Xamarin.Forms からアップグレードするプロジェクトをサポートする Effects のサポートの追加 #1574
  • AppThemeBinding の改良による、ダークテーマとライトテーマのモードのサポート #1657
  • ScrollView ハンドラ #1669
  • Android シェルのコアへの移植 #979
  • 複雑なオブジェクトを渡すシェルのナビゲーション #204
  • XAML ホットリロードのためのビジュアルツリーヘルパーの追加 #1845
  • System.ComponentModel.TypeConverterへの切り替え #1725
  • ウィンドウ ライフサイクル イベント #1754
  • ページナビゲーションイベント #1812
  • CSS プレフィックスを -maui に更新 #1877