方法: MetadataLoadContext を使用してアセンブリの内容を検査する

既定では、開発者は .NET のリフレクション API を使用して、メインの実行コンテキストに読み込まれたアセンブリの内容を検査することができます。 ただし、場合によってはアセンブリを実行コンテキストに読み込むことができないことがあります。その原因としては、それが別のプラットフォームやプロセッサ アーキテクチャ用にコンパイルされた場合や、参照アセンブリである場合などが考えられます。 System.Reflection.MetadataLoadContext API を使用すると、このようなアセンブリを読み込んで検査することができます。 MetadataLoadContext に読み込まれたアセンブリは、メタデータとしてのみ扱われます。つまり、アセンブリ内の型を調べることはできますが、そこに含まれるコードを実行することはできません。 メインの実行コンテキストとは異なり、MetadataLoadContext によって現在のディレクトリから自動的に依存関係が読み込まれることはありません。代わりに、これに渡された MetadataAssemblyResolver によって指定されるカスタム バインド ロジックが使用されます。

必須コンポーネント

MetadataLoadContext を使用するには、System.Reflection.MetadataLoadContext NuGet パッケージをインストールしてください。 これは、.NET Standard 2.0 に準拠している任意のターゲット フレームワーク (たとえば、.NET Core 2.0 や .NET Framework 4.6.1) でサポートされています。

MetadataLoadContext 用の MetadataAssemblyResolver を作成する

MetadataLoadContext を作成するには、MetadataAssemblyResolver のインスタンスを指定する必要があります。 その 1 つを指定するための最も簡単な方法は、PathAssemblyResolver を使用することです。これを使うと、指定したアセンブリ パス文字列のコレクションからアセンブリが解決されます。 直接検査したいアセンブリだけでなく、このコレクションにも、必要な依存関係がすべて含まれている必要があります。 たとえば、ある外部アセンブリに配置されているカスタム属性を読み取るには、そのアセンブリを含める必要があります。そうしないと、例外がスローされます。 ほとんどの場合は、少なくとも "コア アセンブリ"、つまり System.Object などの組み込みのシステム型を含むアセンブリを含める必要があります。 次のコードでは、検査されるアセンブリと、現在のランタイムのコア アセンブリで構成されるコレクションを使用して PathAssemblyResolver を作成する方法が示されています。

var resolver = new PathAssemblyResolver(new string[] { "ExampleAssembly.dll", typeof(object).Assembly.Location });

すべての BCL 型にアクセスする必要がある場合は、コレクションにすべてのランタイム アセンブリを含めることができます。 次のコードでは、検査されるアセンブリと、現在のランタイムのすべてのアセンブリで構成されるコレクションを使用して PathAssemblyResolver を作成する方法が示されています。

// Get the array of runtime assemblies.
string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll");

// Create the list of assembly paths consisting of runtime assemblies and the inspected assembly.
var paths = new List<string>(runtimeAssemblies);
paths.Add("ExampleAssembly.dll");

// Create PathAssemblyResolver that can resolve assemblies using the created list.
var resolver = new PathAssemblyResolver(paths);

MetadataLoadContext の作成

MetadataLoadContext を作成するには、そのコンストラクター MetadataLoadContext(MetadataAssemblyResolver, String) を呼び出します。その際、前に作成した MetadataAssemblyResolver を最初のパラメーターとして渡し、コア アセンブリの名前を 2 番目のパラメーターとして渡します。 コア アセンブリの名前は省略できます。そうした場合、コンストラクターでは、既定の名前 "mscorlib"、"System.Runtime"、または "netstandard" の使用が試みられます。

コンテキストを作成したら、LoadFromAssemblyPath などのメソッドを使用して、そのコンテキストにアセンブリを読み込むことができます。 コードの実行に関連するアセンブリを除き、読み込まれたアセンブリに対してすべてのリフレクション API を使用できます。 GetCustomAttributes メソッドにはコンストラクターの実行が含まれているため、MetadataLoadContext でカスタム属性を調べる必要がある場合は、代わりに GetCustomAttributesData メソッドを使用します。

次のコード サンプルでは、MetadataLoadContext が作成され、それにアセンブリが読み込まれ、コンソールにアセンブリ属性が出力されます。

var mlc = new MetadataLoadContext(resolver);

using (mlc)
{
    // Load assembly into MetadataLoadContext.
    Assembly assembly = mlc.LoadFromAssemblyPath("ExampleAssembly.dll");
    AssemblyName name = assembly.GetName();

    // Print assembly attribute information.
    Console.WriteLine($"{name.Name} has following attributes: ");

    foreach (CustomAttributeData attr in assembly.GetCustomAttributesData())
    {
        try
        {
            Console.WriteLine(attr.AttributeType);
        }
        catch (FileNotFoundException ex)
        {
            // We are missing the required dependency assembly.
            Console.WriteLine($"Error while getting attribute type: {ex.Message}");
        }
    }
}

MetadataLoadContext 内の型について等価性または割り当て可能性をテストする必要がある場合は、そのコンテキストに読み込まれた型オブジェクトのみを使用します。 MetadataLoadContext 型とランタイム型の混在はサポートされていません。 たとえば、MetadataLoadContext 内の型 testedType について考えます。 そこから別の型を割り当てできるかどうかをテストする必要がある場合、typeof(MyType).IsAssignableFrom(testedType) のようなコードは使用しないでください。 代わりに次のようなコードを使用してください。

Assembly matchAssembly = mlc.LoadFromAssemblyPath(typeof(MyType).Assembly.Location);
Type matchType = assembly.GetType(typeof(MyType).FullName!)!;

if (matchType.IsAssignableFrom(testedType))
{
    Console.WriteLine($"{nameof(matchType)} is assignable from {nameof(testedType)}");
}

完全なコード例については、MetadataLoadContext サンプルを使用したアセンブリ内容の検査に関するページを参照してください。