如何:使用 MetadataLoadContext 检查程序集内容

默认情况下,开发人员可以使用 .NET 中的反射 API 检查加载到主执行上下文的程序集内容。 不过,有时无法将程序集加载到执行上下文,例如当程序集是面向其他平台或处理器体系结构编译时,或者它是引用程序集时。 借助 System.Reflection.MetadataLoadContext API,可以加载并检查此类程序集。 加载到 MetadataLoadContext 的程序集仅被作为元数据处理,即可以检查此程序集中的类型,但无法执行其中包含的任何代码。 MetadataLoadContext 不同于主执行上下文,它不会自动加载当前目录中的依赖项;而是使用传递给它的 MetadataAssemblyResolver 所提供自定义绑定逻辑。

先决条件

安装 System.Reflection.MetadataLoadContext NuGet 包,以使用 MetadataLoadContext。 任意 .NET Standard 2.0 兼容的目标框架均支持它,如 NET Core 2.0 或 .NET Framework 4.6.1。

创建 MetadataLoadContext 的 MetadataAssemblyResolver

创建 MetadataLoadContext 需要提供 MetadataAssemblyResolver 的实例。 提供此实例的最简单方法是使用 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 作为第一个参数并将核心程序集名称作为第二个参数传递。 可以省略核心程序集名称,在这种情况下,该构造函数将试图使用默认名称:“mscorlib”、“System.Runtime”或“netstandard”。

创建上下文后,可以使用 LoadFromAssemblyPath 等方法将程序集加载到上下文中。 可以将所有反射 API 用于加载的程序集,涉及代码执行的 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 检查程序集内容示例

另请参阅