我有一个类型类型,我希望在程序集中搜索派生类型。
我出于性能原因试图使用Mono.Cecil预扫描程序集。扫描和加载所有组件花费的时间太长,并且建议使用cecil进行预扫描要快得多,因为只有一小部分可用组件具有匹配类型。
到目前为止,我有以下仅适用于接口。
private static IEnumerable<Type> MatchingTypesFromDll<TParent>(string dllPath)
{
var type = typeof(TParent);
if (!type.IsInterface)
throw new Exception("Only interfaces supported");
try
{
var assDef = Mono.Cecil.AssemblyDefinition.ReadAssembly(dllPath);
var types = assDef.Modules.SelectMany(m => m.GetTypes());
if (types.Any(t => t.Interfaces.Any(i=>i.FullName == type.FullName)))
{
var assembly = Assembly.LoadFrom(dllPath);
return assembly
.GetExportedTypes()
.Where(TypeSatisfies<TParent>);
}
else
{
return new Type[] {};
}
}
catch (Exception e)
{
return new Type[] { };
}
}
private static bool TypeSatisfies<TParent>(Type type)
{
return typeof (TParent).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface;
}
我怎样才能将它扩展为基类的工作?
答案 0 :(得分:2)
主要功能改为
private static IEnumerable<Type> MatchingTypesFromDll<TBaseType>(string dllPath)
{
var type = typeof(TBaseType);
try
{
var hasTypes = Mono.Cecil.AssemblyDefinition
.ReadAssembly(dllPath)
.Modules
.Any
(m =>
{
var td = m.Import(type).Resolve();
return m.GetTypes().Any(t => td.IsAssignableFrom(t));
});
if (hasTypes)
{
var assembly = Assembly.LoadFrom(dllPath);
return assembly
.GetExportedTypes()
.Where(TypeSatisfies<TBaseType>);
}
else
{
return new Type[] {};
}
}
catch (Exception)
{
return new Type[] { };
}
}
和支持的Mono.Cecil代码是定义IsAssignableFrom的地方
static internal class TypeDefinitionExtensions
{
/// <summary>
/// Is childTypeDef a subclass of parentTypeDef. Does not test interface inheritance
/// </summary>
/// <param name="childTypeDef"></param>
/// <param name="parentTypeDef"></param>
/// <returns></returns>
public static bool IsSubclassOf(this TypeDefinition childTypeDef, TypeDefinition parentTypeDef) =>
childTypeDef.MetadataToken
!= parentTypeDef.MetadataToken
&& childTypeDef
.EnumerateBaseClasses()
.Any(b => b.MetadataToken == parentTypeDef.MetadataToken);
/// <summary>
/// Does childType inherit from parentInterface
/// </summary>
/// <param name="childType"></param>
/// <param name="parentInterfaceDef"></param>
/// <returns></returns>
public static bool DoesAnySubTypeImplementInterface(this TypeDefinition childType, TypeDefinition parentInterfaceDef)
{
Debug.Assert(parentInterfaceDef.IsInterface);
return childType
.EnumerateBaseClasses()
.Any(typeDefinition => typeDefinition.DoesSpecificTypeImplementInterface(parentInterfaceDef));
}
/// <summary>
/// Does the childType directly inherit from parentInterface. Base
/// classes of childType are not tested
/// </summary>
/// <param name="childTypeDef"></param>
/// <param name="parentInterfaceDef"></param>
/// <returns></returns>
public static bool DoesSpecificTypeImplementInterface(this TypeDefinition childTypeDef, TypeDefinition parentInterfaceDef)
{
Debug.Assert(parentInterfaceDef.IsInterface);
return childTypeDef
.Interfaces
.Any(ifaceDef => DoesSpecificInterfaceImplementInterface(ifaceDef.Resolve(), parentInterfaceDef));
}
/// <summary>
/// Does interface iface0 equal or implement interface iface1
/// </summary>
/// <param name="iface0"></param>
/// <param name="iface1"></param>
/// <returns></returns>
public static bool DoesSpecificInterfaceImplementInterface(TypeDefinition iface0, TypeDefinition iface1)
{
Debug.Assert(iface1.IsInterface);
Debug.Assert(iface0.IsInterface);
return iface0.MetadataToken == iface1.MetadataToken || iface0.DoesAnySubTypeImplementInterface(iface1);
}
/// <summary>
/// Is source type assignable to target type
/// </summary>
/// <param name="target"></param>
/// <param name="source"></param>
/// <returns></returns>
public static bool IsAssignableFrom(this TypeDefinition target, TypeDefinition source)
=> target == source
|| target.MetadataToken == source.MetadataToken
|| source.IsSubclassOf(target)
|| target.IsInterface && source.DoesAnySubTypeImplementInterface(target);
/// <summary>
/// Enumerate the current type, it's parent and all the way to the top type
/// </summary>
/// <param name="klassType"></param>
/// <returns></returns>
public static IEnumerable<TypeDefinition> EnumerateBaseClasses(this TypeDefinition klassType)
{
for (var typeDefinition = klassType; typeDefinition != null; typeDefinition = typeDefinition.BaseType?.Resolve())
{
yield return typeDefinition;
}
}
}