108 votes

Comment prévenir ReflectionTypeLoadException lors de l'appel de Assembly.GetTypes()

Je cherche à analyser un assemblage à la recherche de types implémentant une interface spécifique en utilisant un code similaire à celui-ci :

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

Mon problème est que j'obtiens une ReflectionTypeLoadException lors de l'appel de asm.GetTypes() dans certains cas, par exemple si l'assemblage contient des types faisant référence à un assemblage qui n'est actuellement pas disponible.

Dans mon cas, je ne suis pas intéressé par les types qui posent problème. Les types que je recherche n'ont pas besoin des assemblages non disponibles.

La question est : est-il possible de passer/ignorer d'une manière ou d'une autre les types qui provoquent l'exception tout en traitant quand même les autres types contenus dans l'assemblage ?

1 votes

Il peut s'agir de bien plus qu'une simple réécriture de ce que vous recherchez, mais MEF vous offre une fonctionnalité similaire. Il vous suffit de marquer chacune de vos classes avec une balise [Export] qui spécifie l'interface qu'elle implémente. Ensuite, vous pourrez importer uniquement les interfaces qui vous intéressent à ce moment-là.

0 votes

@Drew, Merci pour votre commentaire. Je pensais à utiliser MEF, mais je voulais voir s'il y avait une autre solution moins chère.

0 votes

Donner à la classe factory du plugin un nom bien connu afin que vous puissiez simplement utiliser Activator.CreateInstance() directement est une solution de contournement simple. Néanmoins, si vous obtenez cette exception maintenant en raison d'un problème de résolution d'assembly, vous l'obtiendrez probablement aussi plus tard.

138voto

Jon Skeet Points 692016

Une façon assez désagréable serait la suivante :

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

C'est certainement agaçant de devoir faire cela cependant. Vous pourriez utiliser une méthode d'extension pour rendre le code plus agréable dans le code "client" :

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

Vous souhaiterez peut-être déplacer l'instruction return hors du bloc catch - personnellement, je ne suis pas très favorable à ce qu'elle soit là, mais c'est probablement le code le plus court...

2 votes

Merci, cela semble être une solution (et je suis d'accord, cela ne semble pas être une solution propre).

5 votes

Cette solution présente toujours des problèmes lorsque vous essayez d'utiliser la liste des types exposés dans l'exception. Quelle que soit la raison de l'exception de chargement de type, FileNotFound, BadImage, etc, sera toujours lancée à chaque accès aux types en question.

0 votes

@sweetfa : Oui, c'est très limité - mais si l'OP a juste besoin de trouver les noms, par exemple, ça devrait aller.

25voto

sweetfa Points 1378

Alors qu'il semble que rien ne puisse être fait sans recevoir à un moment donné l'exception ReflectionTypeLoadException, les réponses ci-dessus sont limitées en ce sens que toute tentative d'utiliser les types fournis par l'exception donnera toujours un problème avec le problème original qui a causé l'échec du chargement du type.

Pour surmonter cela, le code suivant limite les types à ceux situés dans l'assembly et permet à un prédicat de restreindre davantage la liste des types.

    /// 
    /// Obtenir les types dans l'assembly qui correspondent au prédicat.
    /// par exemple, pour obtenir tous les types dans un espace de noms
    ///     typeof(SomeClassInAssemblyYouWant).Assembly.GetMatchingTypesInAssembly(item => "MyNamespace".Equals(item.Namespace))
    /// 
    /// L'assembly à rechercher
    /// La requête prédicative à laquelle correspondre
    /// La collection de types dans l'assembly qui correspondent au prédicat
    public static ICollection GetMatchingTypesInAssembly(this Assembly assembly, Predicate predicate)
    {
        ICollection types = new List();
        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);
                }
                // Cette liste d'exceptions n'est pas exhaustive, à modifier pour correspondre à toutes les raisons
                // que vous trouvez pour une tentative d'analyse en échec d'un seul assembly
                catch (BadImageFormatException)
                {
                    // Type pas dans cet assembly - référence ailleurs ignorée
                }
            }
        }
        return types;
    }

5voto

Seb Points 2117

Avez-vous envisagé Assembly.ReflectionOnlyLoad ? Étant donné ce que vous essayez de faire, cela pourrait suffire.

2 votes

Oui, j'avais envisagé cela. Mais je ne l'ai pas utilisé car sinon je devrais charger manuellement toutes les dépendances. De plus, le code ne serait pas exécutable avec ReflectionOnlyLoad (voir la section Remarques sur la page que vous avez liée).

1 votes

Assembly.ReflectionOnlyLoad ne peut pas être utilisé dans .NET Core car la plateforme n'est pas prise en charge.

3voto

Sergey Points 31

Dans mon cas, le même problème a été causé par la présence d'assemblages indésirables dans le dossier de l'application. Essayez de vider le dossier Bin et reconstruisez l'application.

3voto

Chiel92 Points 2362

La réponse de Jon Skeet fonctionne bien, mais vous obtenez toujours cette exception lancée en pleine figure à chaque fois. Pour contourner cela, utilisez le snippet suivant et activez "Just My Code" dans les paramètres de débogage de Visual Studio.

[DebuggerNonUserCode]
public static IEnumerable GetSuccesfullyLoadedTypes(Assembly assembly)
{
    try
    {
        return assembly.GetTypes();
    }
    catch (ReflectionTypeLoadException e)
    {
        // Si certains types ne peuvent pas être chargés, cette exception est lancée.
        // Le [DebuggerNonUserCode] s'assure que ces exceptions ne sont pas lancées aux développeurs
        // lorsqu'ils ont activé "Just My Code" dans leurs paramètres de débogage.
        return e.Types.Where(t => t != null);
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X