この記事は、C# その2 Advent Calendar 2018 の9日目の記事です。
AssemblyLoadContextとは
プラグインなどで他のアセンブリを動的に読み込む際そのままでは読み込めないケースがあります。
具体的には
- ホストするアプリケーションと同じライブラリに依存しているがバージョンが違う
- static変数をホストと分ける必要がある
.NET FrameworkではAppDomainを分けて別々にロードすることで解決できました。
しかし.Net Coreではエントリポイントからの実行における複数のAppDomainがサポートされていません 1 。
そこで.Net CoreではAssemblyLoadContextが用意されています。
AssemblyLoadContextが別であれば別アセンブリとして認識されるわけです。
デフォルト空間へのロード
まずは起動時にランタイムによって作られたAssemblyLoadContextへ読み込んでみましょう。
Defautltプロパティで常に参照できます。
var asm = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("System.Xml"));
読み込みメソッドには以下のような種類があります。
Assembly LoadFromAssemblyName(AssemblyName assemblyName)
Assembly LoadFromAssemblyPath(string assemblyPath)
Assembly LoadFromNativeImagePath(string nativeImagePath, string assemblyPath)
Assembly LoadFromStream(Stream assembly)
Assembly LoadFromStream(Stream assembly, Stream assemblySymbols)
使い方はなんとなくわかると思います。わからない場合はソースコード2を読みましょう。
ちなみに記事執筆時点で公式のドキュメントに説明はありません3 4。
カスタムAssemblyLoadContext
AssemblyLoadContextは抽象クラスなので、継承することで新しいAssemblyLoadContextを作ることができます。
継承したクラスでは、読み込まれたアセンブリが依存するアセンブリを解決する際に以下のメソッドが呼ばれます。
Assembly Load(AssemblyName assemblyName);
このメソッド内で独自にアセンブリを読み込めばいいわけです。
読み込むべきアセンブリが見つからない場合はnullを返しましょう。
ちなみにアセンブリが解決されるのは実行に必要になってからです。
必要になるまではGetType()しようがCreateInstance()しようがLoadメソッドは呼ばれません。
アセンブリの解決順序
ランタイムはアセンブリを以下の順序で解決します。
すべてnullを返した場合に例外を発生させます。
- カスタムAssemblyLoadContextのLoadメソッド
- ランタイムでの解決
- デフォルトAssemblyLoadContextのResolvingイベント
- カスタムAssemblyLoadContextのResolvingイベント
Assembly.Load
.NET Frameworkでは、AssemblyクラスのLoadメソッドからアセンブリをロードすることができました。
このメソッドは.Net Standard 2.0に含まれているため.Net Coreでも使用できます。
ただし.NET Frameworkでは同一AppDomainに読み込まれていましたが、.Net Coreでは以下のようになっています4。
- Assembly.Load - Loadメソッドを呼び出したAssemblyと同一のContext
- Assembly.LoadFrom - デフォルトContext
- Assembly.LoadFile - 匿名のコンテキストが作られ、そこに読み込まれる
- Assembly.Load(byte[]) -匿名のコンテキストが作られ、そこに読み込まれる
-
https://docs.microsoft.com/ja-jp/dotnet/core/porting/libraries ↩
-
https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs ↩
-
https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext ↩
-
https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/assemblyloadcontext.md ↩ ↩2