Cómo evitar ReflectionTypeLoadException al llamar a Assembly.GetTypes()


Estoy tratando de escanear un ensamblado en busca de tipos que implementen una interfaz específica utilizando un código similar a este:

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 obtengo un ReflectionTypeLoadException cuando llamo a asm.GetTypes() en algunos casos, por ejemplo, si el ensamblado contiene tipos que hacen referencia a un ensamblado 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 ensamblados no disponibles.

La pregunta es: ¿es posible de alguna manera ¿omitir / ignorar los tipos que causan la excepción pero aún procesan los otros tipos contenidos en el ensamblado?

Author: M4N, 2011-10-25

4 answers

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))
{
    ...
}

Sin embargo, definitivamente es molesto tener que hacer esto. Podría usar un método de extensión para hacerlo más agradable en el código "cliente":

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 posible que desee mover la instrucción return fuera del bloque catch - No estoy muy interesado en que esté allí, pero probablemente es el código más corto...

 110
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-25 14:36:54

Si bien parece que no se puede hacer nada sin recibir la excepción ReflectionTypeLoadException en algún momento, las respuestas anteriores son limitadas en que cualquier intento de utilizar los tipos proporcionados desde la excepción aún dará problema con el problema original que causó que el tipo no se cargara.

Para superar esto, el siguiente código limita los tipos a aquellos ubicados dentro del ensamblado 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;
    }
 18
Author: sweetfa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-16 02:55:59

Has considerado Asamblea.ReflectionOnlyLoad ? Considerando lo que intentas hacer, podría ser suficiente.

 3
Author: Seb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-25 14:55:25

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

 3
Author: Sergey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-03 16:53:44