如何使用Mono.Cecil实现IsAssignableFrom

时间:2016-10-13 10:53:23

标签: c# mono.cecil

我有一个类型类型,我希望在程序集中搜索派生类型。

我出于性能原因试图使用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;
    }

我怎样才能将它扩展为基类的工作?

1 个答案:

答案 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;
      }
   }
}