2011-10-25 15 views
77

Estoy intentando escanear una asamblea para este tipo de aplicación con una interfaz específica utilizando código similar al siguiente:Cómo prevenir ReflectionTypeLoadException al llamar Assembly.GetTypes()

public List<Type> FindTypesImplementing<T>(string assemblyPath) 
{ 
    var matchingTypes = new List<Type>(); 
    var asm = Assembly.LoadFrom(assemblyPath); 
    foreach (var t in asm.GetTypes()) 
    { 
     if (typeof(T).IsAssignableFrom(t)) 
      matchingTypes.Add(t); 
    } 
    return matchingTypes; 
} 

Mi problema es que tengo un ReflectionTypeLoadException al llamar al asm.GetTypes() en algunos casos, p. ej. si el conjunto contiene tipos que hacen referencia a un conjunto que actualmente no está disponible.

En mi caso, no estoy interesado en los tipos que causan el problema. Los tipos que estoy buscando no necesitan los ensamblajes no disponibles.

La pregunta es: ¿es posible omitir/ignorar de algún modo los tipos que causan la excepción pero aún procesar los otros tipos contenidos en el ensamblaje?

+1

Puede ser una reescritura mucho mayor de lo que está buscando, pero MEF le ofrece una funcionalidad similar. Simplemente marque cada una de sus clases con una etiqueta [Exportar] que especifique la interfaz que implementa. Luego puede importar solo aquellas interfaces que le interesen en ese momento. –

+0

@Drew, gracias por tu comentario. Estaba pensando en usar MEF, pero quería ver si hay otra solución más barata. – M4N

+0

Dando a la fábrica de clases de plugins un nombre conocido para que pueda simplemente usar Activator.CreateInstance() directamente es una solución simple. Sin embargo, si obtiene esta excepción ahora debido a un problema de resolución de ensamblaje, es probable que también la obtenga más tarde. –

Respuesta

101

Una forma bastante desagradable sería:

Type[] types; 
try 
{ 
    types = asm.GetTypes(); 
} 
catch (ReflectionTypeLoadException e) 
{ 
    types = e.Types; 
} 
foreach (var t in types.Where(t => t != null)) 
{ 
    ... 
} 

Definitivamente es molesto tener que hacer esto sin embargo. Se podría utilizar un método de extensión para que sea más agradable en el "cliente" código:

public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) 
{ 
    // TODO: Argument validation 
    try 
    { 
     return assembly.GetTypes(); 
    } 
    catch (ReflectionTypeLoadException e) 
    { 
     return e.Types.Where(t => t != null); 
    } 
} 

es muy posible que desee mover la declaración return fuera del bloque catch - No estoy terriblemente interesado en que sea yo mismo , pero probablemente es el código más corto ...

+2

Gracias, parece ser una solución (y estoy de acuerdo, no parece ser una solución limpia). – M4N

+3

Esta solución todavía tiene problemas cuando intenta utilizar la lista de tipos expuestos en la excepción. Independientemente del motivo de la excepción de carga de tipo, FileNotFound, BadImage, etc., seguirá arrojando en cada acceso a los tipos que están en cuestión. – sweetfa

+0

@sweetfa: Sí, es muy limitado, pero si el OP solo necesita encontrar los nombres, por ejemplo, debería estar bien. –

3

Ha considerado Assembly.ReflectionOnlyLoad? Teniendo en cuenta lo que estás tratando de hacer, podría ser suficiente.

+1

Sí, lo había considerado. Pero no lo usé porque de lo contrario tendría que cargar manualmente cualquier dependencia. Además, el código no sería ejecutable con ReflectionOnlyLoad (consulte la sección Comentarios en la página que ha vinculado). – M4N

16

Si bien parece que no se puede hacer nada sin recibir la ReflectionTypeLoadException en algún momento, las respuestas anteriores están limitadas en el sentido de que cualquier intento de utilizar los tipos provistos de la excepción todavía dará problemas con el problema original que causó el tipo no se carga

Para solucionar este problema, el siguiente código limita los tipos a aquellos ubicados dentro del ensamblaje y permite que un predicado restrinja aún más la lista de tipos.

/// <summary> 
    /// Get the types within the assembly that match the predicate. 
    /// <para>for example, to get all types within a namespace</para> 
    /// <para> typeof(SomeClassInAssemblyYouWant).Assembly.GetMatchingTypesInAssembly(item => "MyNamespace".Equals(item.Namespace))</para> 
    /// </summary> 
    /// <param name="assembly">The assembly to search</param> 
    /// <param name="predicate">The predicate query to match against</param> 
    /// <returns>The collection of types within the assembly that match the predicate</returns> 
    public static ICollection<Type> GetMatchingTypesInAssembly(this Assembly assembly, Predicate<Type> predicate) 
    { 
     ICollection<Type> types = new List<Type>(); 
     try 
     { 
      types = assembly.GetTypes().Where(i => i != null && predicate(i) && i.Assembly == assembly).ToList(); 
     } 
     catch (ReflectionTypeLoadException ex) 
     { 
      foreach (Type theType in ex.Types) 
      { 
       try 
       { 
        if (theType != null && predicate(theType) && theType.Assembly == assembly) 
         types.Add(theType); 
       } 
       // This exception list is not exhaustive, modify to suit any reasons 
       // you find for failure to parse a single assembly 
       catch (BadImageFormatException) 
       { 
        // Type not in this assembly - reference to elsewhere ignored 
       } 
      } 
     } 
     return types; 
    } 
1

En mi caso, el mismo problema fue causado por la presencia de ensamblajes no deseados en la carpeta de la aplicación. Intente borrar la carpeta Bin y reconstruir la aplicación.

Cuestiones relacionadas