#c# #mono.cecil
#c# #mono.cecil
Вопрос:
У меня есть тип Type, который я хочу искать в сборке для производных типов.
Я пытаюсь использовать 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)
amp;amp; !type.IsAbstract
amp;amp; !type.IsInterface;
}
Как я мог бы расширить это, чтобы оно работало и для базовых классов?
Комментарии:
1. Что вы имеете
normal
в виду, когда ссылаетесь на класс?2. Я имею в виду не интерфейс.
3. Изменена формулировка «обычные классы» на «базовые классы»
Ответ №1:
Основная функция изменена на
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
amp;amp; 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 amp;amp; 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;
}
}
}
Комментарии:
1. Я не думаю, что сравнение
MetadataToken
— хорошая идея. Типы могут поступать из разных сборок.
Ответ №2:
Я согласен с комментарием Маркса:
Я не думаю, что сравнение MetadataToken — хорошая идея. Типы могут поступать из разных сборок.
Метаданные, похоже, уникальны только в пределах одной сборки. У меня была проблема с двумя совершенно разными типами из разных сборок, имеющими один и тот же метаданный. Я предлагаю также проверить соответствие полному имени. Сравнение полного имени само по себе является неплохой проверкой. Вместе с MetadataToken я думаю, что сравнение надежно.
Обновленное решение:
internal static 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
amp;amp; childTypeDef.EnumerateBaseClasses().Any(b => Equals(b, parentTypeDef));
/// <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.InterfaceType.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 Equals(iface0, iface1) || 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
|| Equals(target, source)
|| source.IsSubclassOf(target)
|| target.IsInterface amp;amp; source.DoesAnySubTypeImplementInterface(target);
/// <summary>
/// Enumerate the current type, it's parent and all the way to the top type
/// </summary>
/// <param name="classType"></param>
/// <returns></returns>
public static IEnumerable<TypeDefinition> EnumerateBaseClasses(this TypeDefinition classType)
{
for (var typeDefinition = classType; typeDefinition != null; typeDefinition = typeDefinition.BaseType?.Resolve())
{
yield return typeDefinition;
}
}
public static bool Equals(TypeDefinition a, TypeDefinition b)
{
return
a.MetadataToken == b.MetadataToken
amp;amp; a.FullName == b.FullName;
}
}