Special Windows 10 issue 2015

Volume 30 Number 11

Microsoft .NET - .NET とユニバーサル Windows プラットフォーム開発

Daniel Jacobson | Windows 2015

Visual Studio 2015 では、最新 .NET テクノロジを利用して、ユニバーサル Windows プラットフォーム (UWP) アプリケーションをビルドできるようになります。UWP アプリケーションとは、ポケットの中のスマートフォン、かばんの中のノート PC やタブレット、オフィスの Surface Hub、自宅の Xbox 本体、HoloLens、モノのインターネット (IoT) といって思い付くデバイスなど、あらゆる Windows 10 デバイスで実行できるアプリケーションです。Windows 開発者にとっては本当に刺激的な時代です。

UWP の新機能

.NET 開発者にとっては、UWP が提供するものすべてに価値があります。UWP アプリは、Windows 10 にアップグレードされた (またはアップグレード予定の) 膨大な数のデスクトップ上で "ウィンドウ" モードで実行されます。1 つのアプリケーション パッケージかつ 1 つのコード ベースで、すべての Windows 10 デバイスに UWP アプリを提供できます。さらに、新しい Microsoft .NET Core Framework を利用します (後ほど詳しく説明します)。作成した .NET ビジネス ロジックは、.NET Core をサポートする他のプラットフォーム (ASP.NET 5 など) で実行できます。UWP アプリは、アプリと一緒に .NET Core の小さなコピーも配置するため、アプリは常にテスト済みの .NET バージョンに対して実行されることになります。すべての .NET UWP アプリは .Net ネイティブを最大限に利用します。.Net ネイティブは、高度に最適化されたネイティブ マシン コードを生成するため、パフォーマンスが向上します (後ほど説明します)。

.NET Core Framework

.NET Core Framework は、最新デバイスやクラウド ワークロードを対象とする新しいバージョンの .NET です。Microsoft .NET Framework の汎用のモジュール形式の実装で、多種多様な環境でさまざまなワークロード向けに移植および使用できます。また、.NET Core Framework はオープン ソースで、GitHub (github.com/dotnet/corefx、英語) からダウンロードでき、Windows、Linux、および Mac OS X についてはマイクロソフトがサポートを提供します。そのため、.NET の最新テクノロジを利用する UWP 開発者にはさまざまなメリットがあります。Visual Studio 2015 では、.NET Core ポータブル クラス ライブラリ (PCL) を利用して、すべての UWP アプリ、.NET 4.6 アプリ、ASP.NET 5 アプリを始めとし、クロス プラットフォーム アプリであってもターゲットにできます。

さらに、.NET Core Framework は .NET API のスーパーセットです。.NET API は、以前、 Windows ストア アプリ開発に利用していたものです。つまり、UWP 開発者は、自身の API で追加名前空間をいくつか利用できるようになります。このような名前空間の 1 つが、UDP 通信に使用する System.Net.Sockets です。これは以前、Windows Runtime (WinRT) アプリで利用できなかったため、回避策として WinRT 固有の UDP API を使用していました。ソケットを .NET Core で利用できるようになるため、UWP アプリや他の .NET アプリに同じソケット コードを使用できるようになります。

ほかにも、System.Net.Http.HttpClient API が、WinRT HTTP のスタック上にビルドされています。これにより、サーバーで HTTP/2 がサポートされていれば、既定で HTTP/2 を使用でき、待機時間の短縮やラウンド トリップ通信の減少につながります。

Windows Communication Foundation (WCF) クライアント (および関連する [サービス参照の追加] ダイアログ) は以前、Windows Phone .appx プロジェクトでは利用できませんでしたが、.NET Core の一部になったため、すべての .NET UWP アプリで使用できるようになります。

最後に、.NET Core は .NET ネイティブの基盤となるフレームワークです。.NET ネイティブの設計当時、フレームワーク クラスのライブラリとしては明らかにふさわしくありませんでした。それは、.NET ネイティブはフレームワークとアプリケーションを静的にリンクしてから、アプリケーションに不要な部分を削除するためです (おおまかですが、考え方はお分かりいただけるでしょう。詳細については、「.NET ネイティブの詳細」(bit.ly/1UR7ChW、英語) を参照してください)。

従来の .NET Framework の実装は細分化されていなかったため、リンカーには、アプリケーションにコンパイルするフレームワークの量を削減することが困難でした。その結果生まれた .NET Core は、事実上 .NET Framework から分化したフレームワークで、細分化に関する懸念事項を最適化した実装になっています。このように細分化された実装には、.NET Core Framework を一連の NuGet パッケージとして配布できるというメリットも生まれ、.NET Framework の帯域外で個別のクラスを更新できるようになります。先に進む前に、NuGet の変更点についても触れておきます。

NuGet の新機能

UWP には NuGet 3.1 のサポートが組み込まれます。このリリースには、パッケージの依存関係管理を強化する機能や、複数のプロジェクトでの再利用を目的としたパッケージのローカル キャッシュなどが含まれています。

NuGet 3.1 では、パッケージ依存関係の宣言モデルが新しくなっています。ASP.NET 5 から NuGet は project.json ファイルのサポートを導入しましたが、これは UWP がサポートするのと同じモデルです。Project.json により、開発者はプロジェクトの依存関係を記述して、直接依存するパッケージを明確に定義できるようになります。フォーマットは ASP.NET 5 も UWP も同じなので、同じファイルを使用して、両方のプラットフォームや PCL のパッケージ参照を定義できます。

packages.config を project.json に変えることで、プロジェクトで参照を "付け替える" ことができるようになります。つまり、NuGet の依存関係は新たに推移的になります。これまでは別の NuGet パッケージを参照するパッケージを参照する場合、パッケージのバージョン管理が困難でした。たとえば、NHibernate というパッケージがあり、このパッケージは lesi.Collections パッケージに依存するとします。この場合、packages.config は、パッケージごとに 1 つずつ 2 つの参照を保持します。NHibernate を更新する場合は、lesi.collections も更新するべきでしょうか。あるいは、lesi.collections が更新されている場合、新しい機能をサポートするためには NHibernate も更新するべきでしょうか。これは複雑なサイクルになるため、パッケージのバージョン管理が難しくなります。NuGet の推移的依存関係機能により、パッケージのバージョン管理が困難だという考えはなくなり、パッケージの定義ファイル (nuspecs) で改良されたセマンティックなバージョン管理を使用してパッケージ参照を更新できるようになります。

また、NuGet は、%userprofile%\.nuget\packages フォルダーにあるグローバル パッケージのフォルダーに、使用するパッケージのコピーをダウンロードおよび格納するようになります。これにより、それぞれのパッケージを 1 回ダウンロードするだけでよくなり、パッケージ参照のパフォーマンスが向上します。また、プロジェクト間で同じパッケージのバイナリを共有できるようになるため、ワークステーションで使用するディスク領域を削減できます。

NuGet と .NET Core

集中的に細分化を行った .NET Core と、NuGet 3.1 のパッケージ依存関係管理が組み合わさると、残りの .NET Framework の帯域外にある個別の .NET Framework パッケージを更新できるようになります。UWP と .NET Core は、NuGet パッケージのセットとしてアプリに含まれます。新しいプロジェクトを作成するときは、全般的な Microsoft.NETCore.UniversalWindowsPlatform パッケージ依存関係のみが確認されますが、NuGet でこのパッケージを見ると、含まれる .NET Framework ライブラリがすべて確認されます (図 1 参照)。

NuGet での .NET Framework ライブラリの表示
図 1 NuGet での .NET Framework ライブラリの表示

たとえば、アプリケーションで使用する新しい API を導入するように、System.Net.Sockets クラスが更新されているとします。従来の .NET では、アプリケーションは、.NET Framework 全体の新しいビルドでの依存関係を利用する必要があります。UWP、.NET Core、NuGet の組み合わせでは、そのパッケージの最新バージョンだけを含めるように NuGet の依存関係を更新できます。その後、アプリケーションをコンパイルしてパッケージ化すると、そのバージョンのフレームワーク ライブラリがアプリケーションに含められます。その結果、常に最新のフレームワークをデバイスにインストールするようユーザーに求めることなく、最新の最も優れた .NET テクノロジを使用する柔軟性が得られます。

開発者独自のタイミングで .NET クラスを更新できるようになるだけでなく、コンポーネント化された .NET Core によって、.NET ネイティブも有効になるため、顧客デバイスへの提供時に、すべての Windows 10 C# アプリケーションや Visual Basic アプリケーションのパフォーマンスが向上します。

.NET ネイティブとは

.NET Core Framework によって .NET ネイティブが有効になることがわかったところで、UWP 開発者にとっての .NET ネイティブの利用価値を考えてみます。

.NET ネイティブは、ahead-of-time (AOT) コンパイル プロセスです。つまり、コンパイル時にマネージ .NET コードをネイティブ マシン コードに変換します。これに対して、従来の .NET は just-in-time (JIT) コンパイルを使用し、初回実行時までメソッドのネイティブ コンパイルを遅延します。.NET ネイティブはより C++ コンパイラに近くなります。実際、ツール チェーンの一環として Visual Studio C++ コンパイラを使用します。すべてのマネージ (C# または Visual Basic) UWP は、この新しいテクノロジを利用します。アプリケーションは、ユーザー デバイスにインストールされる前に、自動的にネイティブ コードにコンパイルされます。このしくみの詳細については、MSDN ライブラリの記事「.NET ネイティブによるアプリのコンパイル」(https://msdn.microsoft.com/ja-jp/library/dn584397(v=vs.110).aspx) を参考にしてください。

.NET ネイティブが開発者とアプリへの影響

状況にもよりますが、一般には、アプリの起動速度が上がり、パフォーマンスが向上し、使用するシステム リソース量は減少します。アプリケーションの初回起動時のパフォーマンスが最大 60% 向上し、2 回目以降の起動 (ウォーム起動) 時のパフォーマンスが最大 40% 向上すると見込まれます。アプリケーションをネイティブにコンパイルするため、アプリケーションの容量が少なくなります。.NET ランタイムと依存関係がすべて取り除かれるため、エンド ユーザーは、セットアップ エクスペリエンスを中断することなく、アプリが参照する特定のバージョンの .NET Framework を取得できます。実際には、.NET 依存関係がすべてアプリケーション内にパッケージ化されるため、コンピューターにインストールされる .NET Framework が変わっても、アプリの動作は変わりません。

アプリケーションはネイティブ バイナリにコンパイルされますが、使い慣れた .NET 言語 (C# や Visual Basic) とその言語に関連付けられる優れたツールは依然として利用できます。最後に、ビジネス ロジック用拡張 API、組み込みメモリ管理、例外処理を備えた、.NET Framework で利用可能な包括的で一貫性のあるプログラミング モデルを引き続き使用できます。

.NET ネイティブにより、マネージ開発にも C++ のパフォーマンスにも最高の結果が得られます。

コンパイル構成、デバッグとリリースの違い

.NET ネイティブのコンパイルは複雑なプロセスになるため、従来の .NET コンパイルよりも少し時間がかかります。前述のメリットは、コンパイル時間の犠牲のうえに成り立っています。アプリ実行時に毎回ネイティブにコンパイルすることもできますが、ビルド完了までの待機時間が余計にかかります。Visual Studio ツールは、この問題に対処し、できる限りスムーズな開発者エクスペリエンスを生み出すように設計されています。

"デバッグ" 構成でビルドおよび実行するときは、アプリケーション内にパッケージ化される CoreCLR に対して中間言語コードを実行しています。.NET システムのアセンブリは、アプリケーション コードと共にパッケージ化されるため、アプリケーションは Microsoft.NET.CoreRuntime (CoreCLR) パッケージとの依存関係が生じます。CoreCLR フレームワークがテスト デバイスにインストールされていなければ、Visual Studio は自動的にこれを検出して、アプリケーションの配置前にインストールを実行します。

つまり、迅速なコンパイルと配置、リッチなデバッグと診断、.NET 開発で使い慣れたすべてのツールなど、最大限の開発エクスペリエンスを利用できるようにします。

"リリース" モードに切り替えると、アプリは既定で .NET ネイティブ ツールチェーンを使用します。パッケージはネイティブのバイナリにコンパイルされるため、パッケージに .NET Framework ライブラリを含める必要はありません。さらに、コンパイル済みパッケージは、CoreCLR パッケージとは対照的に、最新のインストール済み .NET ネイティブ ランタイムに依存します。デバイスの .NET ネイティブ ランタイムは、常にアプリケーション パッケージと互換性があります。

"リリース" 構成でのローカル ネイティブ コンパイルでは、顧客が利用するのに似た環境でアプリケーションをテストできるようになります。開発を進めながら、アプリケーションを定期的にテストすることは重要です。顧客が利用するコード生成テクノロジやランタイム テクノロジを使用してアプリケーションをテストすることにより、可能性のあるすべてバグ (パフォーマンス特性が異なるために生じる競合状態など) に対処できるようにします。

目安としては、このように開発を進めながら定期的にアプリケーションをテストして、.NET ネイティブ コンパイラに起因するすべての問題を特定して解決するようにします。まったく問題が見つからないこともよくありますが、.NET ネイティブとうまく連携しない機能もいくつかあります。その一例が 4 次元以上の配列です。最終的に、顧客の手元には .NET ネイティブにコンパイルされたバージョンのアプリケーションがリリースされることになるため、開発を進めながらこのバージョンのアプリケーションをテストしてからリリースすることをお勧めします。

.NET ネイティブ コンパイルでは、AnyCPU ビルド構成がなくなっています。.NET ネイティブでは、ネイティブ コンパイルがアーキテクチャに依存するため、AnyCPU は有効なビルド構成ではなくなります。そのため、アプリケーションをパッケージ化する際、3 つのアーキテクチャ構成 (x86、x64、および ARM) をすべて選択して、できる限り多くのデバイスにアプリケーションを適用できるようにします。結局のところ、これが UWP です。

ただし、AnyCPU 構成のライブラリや DLL をビルドして、UWP アプリから参照することはできます。このようなコンポーネントは、そのコンポーネントを利用するプロジェクトの構成 (.appx) に応じて、アーキテクチャ固有のバイナリにコンパイルされます。

クラウドでの .NET ネイティブ

.NET ネイティブの優れた機能の 1 つは、コンパイラをクラウドでホストできることです。つまり、アプリケーションにメリットがある改良がコンパイラに加えられた場合、ストアのクラウド ホスト型 .NET ネイティブ コンパイラでアプリケーション パッケージを再コンパイルして、新たなメリットを利用することができます。このようなコンパイルを実行するときは、開発者が意識していなくてもこのようなメリットが取り込まれるため、最終的には、アプリケーションのユーザーの満足度が向上します。

ただし、これは開発者のワークフローにいくつか影響を与えます。たとえば、最新のツールを常にインストールして、最新のローカル バージョンのコンパイラ対して .NET ネイティブ コンパイルをテストできるようにしておく必要があります。また、Visual Studio でストア パッケージをビルドすると、.appxupload とサイドロード用の "test".appx という、2 つのパッケージが作成されます。.appxupload には、MSIL バイナリだけでなく、アプリで利用する .NET ネイティブ ツールチェーンのバージョンに対する明示的な参照 (AppxManifest.xml では "ilc.exe" として参照) が含まれます。その後このパッケージがストアに配置され、まったく同じバージョンの .NET ネイティブ ツールチェーンを使用してコンパイルされます。コンパイラはクラウド ホスト型なので、アプリをローカルで再コンパルする必要なく、この作業を繰り返してバグを修正できます。

.NET ネイティブでは、ストアにアップロードするパッケージに注意する必要があります。ストアが開発者の代わりにネイティブ コンパイルを実行するため、ローカルの .NET ネイティブ コンパイラで生成したネイティブ バイナリをアップロードすることができません。Visual Studio のワークフローでは、こうしたプロセス全体がガイドされるため、適切なパッケージを選択できます。ストア パッケージを作成する際の詳細なガイダンスについては、MSDN ライブラリの記事「Windows 10 のユニバーサル Windows アプリをパッケージ化する」(https://msdn.microsoft.com/ja-jp/library/hh454036.aspx) を参照してください。ここで示されているはパッケージの作成プロセスに従えば、ストアにアップロードする適切なパッケージを生成および選択できるようになります。

.NET ネイティブによるデバッグ

.NET ネイティブが原因と考えられる問題が見つかった場合、問題のデバッグに役立つテクニックがあります。リリース構成では、既定でコードが完全に最適化される (たとえば、多くの場所でコードのインライン化が行われる) ため、デバッグの成果物が一部失われます。その結果、リリース構成アプリのデバッグは困難になる可能性があります。たとえば、予期しないステップ動作やブレークポイント動作が発生したり、メモリの最適化が原因で変数を検査できないこともあります。リリース構成では、コードの最適化以外に、既定で .NET ネイティブ コンパイラを使用するため、.NET ネイティブのコンパイル プロセスに起因する問題をデバッグすることも困難です。

このような問題を回避するための優れた方法は、.NET ネイティブ コンパイラを使用しても、コードを完全に最適化しない、カスタム ビルド構成をプロジェクト用に作成することです。カスタム ビルド構成を作成するには、ビルド構成ドロップダウンの構成マネージャーを開きます (図 2 参照)。

構成マネージャーを開く
図 2 構成マネージャーを開く

[アクティブ ソリューション構成] ドロップダウンで、[<新規作成>] を選択して新しい構成を作成します (図 3 参照)。

新しい構成の作成
図 3 新しい構成の作成

新しい構成に、後で使用しやすい名前を入力します。ここでは、「Debug .NET Native」を使用します。"リリース" ビルド構成の設定をコピーして、[OK] をクリックします。

構成マネージャーを閉じた後、ソリューション エクスプローラーでプロジェクトを右クリックし、[プロパティ] をクリックして、プロジェクトのプロパティ ページを開きます。[ビルド] タブに移動した後、[.NET ネイティブ ツール チェーンでコンパイルする] チェック ボックスがオンになっていて、[コードの最適化] チェック ボックスがオフになっていることを確認します (図 4 参照)。

.NET ネイティブをデバッグするためのビルド構成の作成
図 4 .NET ネイティブをデバッグするためのビルド構成の作成

これで、.NET ネイティブ固有の問題をデバッグするために使用できるビルド構成を用意することができます。

.NET ネイティブを使用したデバッグに関する詳細については、MSDN ライブラリの記事「.NET ネイティブ Windows ユニバーサル アプリのデバッグ」(bit.ly/1Ixd07v、英語) を参照してください。

.NET ネイティブ アナライザー

問題をデバッグする方法を理解することも重要ですが、最初から問題を回避できればそれに越したことはありません。アプリケーションで NuGet を利用して Microsoft.NETNative.Analyzer (bit.ly/1LugGnO、英語) をインストールすることができます。パッケージ マネージャー コンソールで、Install-Package Microsoft.NETNative.Analyzer コマンドを実行して、このパッケージをインストールできます。開発時、.NET ネイティブ コンパイラと互換性がないコードには、このアナライザーが警告を表示します。.NET のサーフェスのごく一部には、互換性がない部分がありますが、大半のアプリでは問題になりません。

まとめ

.NET Windows 開発者にとっては刺激的な時代になりました。UWP、.NET ネイティブ、および NuGet に対する変更により、顧客が利用する多種多様なデバイスを対象にしたアプリをかつてないほど簡単に作成できるようになります。初めて、すべての .NET クラスで最新のメリットを利用できるようになり、アプリケーションをすべての Windows 10 デバイスで実行できるようになります。


Daniel Jacobsonは、Windows プラットフォーム開発者向けツールに取り組む Visual Studio のプログラム マネージャーです。連絡先は dajaco@microsoft.com (英語のみ) です。

この記事のレビューに協力してくれた技術スタッフの Kino Aguilar、Adam Denning、Yishai Galatzer、Jenny Hayes、Jeremy Meng、Harikrishna Menon、Jessica Prince、Unni Ravindranathan、Navit Saxena、Michael Strehovsky、Debbie Thorn、Matthew Whilde、および Lucian Wischik に心より感謝いたします。