动态类型解析 - 多次调用 Type.GetType 或扫描程序集中的所有类型?
Dynamic type resolution - multiple calls to Type.GetType or scanning all types in assembly?
我目前正在实施类型名称解析方案。可用信息与您在常规 C# 源项目中获得的信息非常相似:
Assembly
个参考列表
using
个命名空间列表
在 运行 时,对于每个要解析的类型名称,该名称要么是完全限定的(带有命名空间,但不带有程序集名称),要么是预计来自以下之一的简单名称using
个命名空间。
为了找到与每个标识符匹配的 Type
,我正在考虑以下两种策略之一:
使用 Assembly.Load
预加载所有程序集并扫描所有类型。所有具有与 using
命名空间之一匹配的命名空间前缀的简单名称都将被预缓存。还创建了一个字典,将程序集中每个命名空间限定的类型名称直接映射到它的 Type
。加载阶段会进行相应的冲突解决
不要预加载任何东西。每当类型名称到达时,请尝试以下顺序:
假设名称是完全限定的;依次连接每个引用的程序集名称以创建程序集限定名称并调用 Type.GetType
以查看我们是否获得有效类型。
如果上述步骤没有产生任何有效的类型并且名称没有前缀,则假定它是一个简单的名称;重复上述步骤,但每次都在简单名称前加上 using
命名空间之一,以查看我们是否获得有效类型。
哪种方法更可取,优缺点是什么?
目前还不清楚每个 运行 需要以这种方式解析多少类型,但我假设在 10 到 100 之间。可以在任何时候引用多个程序集,每个程序集可能有数百种类型。
我有兴趣了解这两种策略的相对表现,也很想知道处理这种情况的现有最佳实践。除了性能之外,了解行为副作用的任何差异将非常有帮助:例如,扫描 Assembly
中的所有类型是否会执行加载类型的所有静态构造函数?我更喜欢一种在 运行 引用程序集中的任何代码中尽可能惰性的方法。
事实证明,在新的 .NET Standard 中有一种更好的方法,即使用新的 System.Reflection.Metadata
命名空间。用他们自己的话说:
"This packages provides a low-level .NET (ECMA-335) metadata reader.
It's geared for performance and is the ideal choice for building
higher-level libraries that intend to provide their own object model,
such as compilers."
这是一个小代码片段,用于列出给定程序集文件中所有类型定义的名称和命名空间:
using System;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
namespace MetadataTest
{
class Program
{
static void Main(string[] args)
{
using (var stream = File.OpenRead(args[0]))
using (var peFile = new PEReader(stream))
{
var metadataReader = peFile.GetMetadataReader();
foreach (var type in metadataReader.TypeDefinitions)
{
var definition = metadataReader.GetTypeDefinition(type);
var name = metadataReader.GetString(definition.Name);
var ns = metadataReader.GetString(definition.Namespace);
Console.WriteLine(ns + "." + name);
}
}
}
}
}
我目前正在实施类型名称解析方案。可用信息与您在常规 C# 源项目中获得的信息非常相似:
Assembly
个参考列表using
个命名空间列表
在 运行 时,对于每个要解析的类型名称,该名称要么是完全限定的(带有命名空间,但不带有程序集名称),要么是预计来自以下之一的简单名称using
个命名空间。
为了找到与每个标识符匹配的 Type
,我正在考虑以下两种策略之一:
使用
Assembly.Load
预加载所有程序集并扫描所有类型。所有具有与using
命名空间之一匹配的命名空间前缀的简单名称都将被预缓存。还创建了一个字典,将程序集中每个命名空间限定的类型名称直接映射到它的Type
。加载阶段会进行相应的冲突解决不要预加载任何东西。每当类型名称到达时,请尝试以下顺序:
假设名称是完全限定的;依次连接每个引用的程序集名称以创建程序集限定名称并调用
Type.GetType
以查看我们是否获得有效类型。如果上述步骤没有产生任何有效的类型并且名称没有前缀,则假定它是一个简单的名称;重复上述步骤,但每次都在简单名称前加上
using
命名空间之一,以查看我们是否获得有效类型。
哪种方法更可取,优缺点是什么?
目前还不清楚每个 运行 需要以这种方式解析多少类型,但我假设在 10 到 100 之间。可以在任何时候引用多个程序集,每个程序集可能有数百种类型。
我有兴趣了解这两种策略的相对表现,也很想知道处理这种情况的现有最佳实践。除了性能之外,了解行为副作用的任何差异将非常有帮助:例如,扫描 Assembly
中的所有类型是否会执行加载类型的所有静态构造函数?我更喜欢一种在 运行 引用程序集中的任何代码中尽可能惰性的方法。
事实证明,在新的 .NET Standard 中有一种更好的方法,即使用新的 System.Reflection.Metadata
命名空间。用他们自己的话说:
"This packages provides a low-level .NET (ECMA-335) metadata reader.
It's geared for performance and is the ideal choice for building higher-level libraries that intend to provide their own object model, such as compilers."
这是一个小代码片段,用于列出给定程序集文件中所有类型定义的名称和命名空间:
using System;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
namespace MetadataTest
{
class Program
{
static void Main(string[] args)
{
using (var stream = File.OpenRead(args[0]))
using (var peFile = new PEReader(stream))
{
var metadataReader = peFile.GetMetadataReader();
foreach (var type in metadataReader.TypeDefinitions)
{
var definition = metadataReader.GetTypeDefinition(type);
var name = metadataReader.GetString(definition.Name);
var ns = metadataReader.GetString(definition.Namespace);
Console.WriteLine(ns + "." + name);
}
}
}
}
}