.NET Frameworkと.NET Core
前世紀の最後の年に.NET Frameworkが発表されてから20年ほどになります。その基本的な構造は、2014年に発表された.NET Coreでも変わりません。
以降では、「.NET Frameworkと.NET Coreに共通して」と長たらしく書く代わりに「.NET Framework/Core」と略します。
.NET Framework/Coreの構造
.NET Framework/Coreは、CLR(Common Language Runtime、共通言語ランタイム)とクラスライブラリー群から構成されています(次の図)。
CLRは仮想マシンのようなものです。後で述べるCIL(Common Intermediate Language、共通中間言語)を解釈してネイティブコード(CPUに対する命令)に変換し、演算を実行したりOSの機能を呼び出したりします。CLRの大きな特徴としてメモリー管理があります。CLRの管理下で動作するコードはマネージドコード(managed code)と呼ばれ、メモリーの確保と解放に煩わされることはありません。
クラスライブラリーには、基盤となるBCL(Base Class Libraries、基本クラスライブラリー/基底クラスライブラリー)と、BCLを基にして構築された上位レベルの各種ライブラリーがあります。上位レベルのライブラリーはフレームワークと呼ばれることもあります。データベースを扱うEntity Framework(EF)やWebアプリをサーバーで実行するASP.NET、そしてGUIフレームワークであるWindows FormsやWPFなど、多数のライブラリーが提供されています。
.NET Frameworkと.NET Coreの大きな違いは、OSの部分が.NET FrameworkではMicrosoftのOSだけだったものが、.NET Coreでは(本稿執筆時点で)macOSとLinuxもサポートされていることです。なお、現在は区別のために、.NET CoreのCLRは「CoreCLR」と、.NET CoreのBCLは「CoreFX」と呼ばれています。
より詳しくは、docs.microsoft.comにある次のドキュメントをご覧ください。
ソースコードから実行まで
.NET Framework/Core用に書いたソースコードが実行されるまでの流れは、基本的には次の図のようになっています。
.NET Framework/Coreでは、ソースコードをコンパイルするとCIL(Common Intermediate Language、共通中間言語)になります。CILは、ただILとも、MSILとも呼ばれます(上図では「IL」と表記)。コンパイルの出力は、Windowsでは*.exe/*.dllになりますが、その中身はCPUに依存したネイティブコードではなくて、.NET Framework/Coreで定義されているILなのです。
そして、CLR(共通言語ランタイム)はJIT(Just-In-Time)コンパイラーを使って、ILをネイティブコード(上図では「NC」と表記)に変換しながら、変換したネイティブコードを実行します。
より詳しくは、docs.microsoft.comにある次のドキュメントをご覧ください。
以上のようなILのバイナリーファイルを配布するやり方は、1種類のバイナリーファイルだけで複数種類のCPUに対応できるというメリットがあります。しかし、アプリの実行時にJITコンパイルするのは、パフォーマンスの面では好ましくありません。そこで、事前にネイティブコードへコンパイルしておく方法も提供されています(AOT:Ahead-Of-Timeコンパイル)。それには2通りあって、1つはアプリのインストール先で行うもの、もう1つはアプリを配布する前に行うものです。.NET Frameworkには従来から、アプリのインストール先での事前コンパイルを行うNgenが提供されてきました。.NET Framwork 4.7.2からはアプリ配布前にコンパイルを行うNGenReadyToRunも利用できます。.NET Coreでは、アプリ配布前にコンパイルを行うCrossGenとReadyToRun(R2R)が提供されています。また、UWPアプリ用としてアプリ配布前にコンパイルを行う.NET Nativeもあります。