Jak zapobiec ReflectionTypeLoadException podczas wywoływania Assembly.GetTypes ()


97

Próbuję przeskanować zestaw pod kątem typów implementujących określony interfejs przy użyciu kodu podobnego do tego:

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

Mój problem polega na tym, że ReflectionTypeLoadExceptionpodczas wywoływania asm.GetTypes()w niektórych przypadkach otrzymuję a , np. Jeśli zestaw zawiera typy odwołujące się do zestawu, który jest obecnie niedostępny.

W moim przypadku nie interesują mnie typy, które powodują problem. Typy, których szukam, nie wymagają niedostępnych zestawów.

Pytanie brzmi: czy można w jakiś sposób pominąć / zignorować typy, które powodują wyjątek, ale nadal przetwarzać inne typy zawarte w zestawie?


1
Może to być znacznie więcej przepisywania niż tego, czego szukasz, ale MEF zapewnia podobną funkcjonalność. Po prostu oznacz każdą z klas znacznikiem [Export], który określa interfejs, który implementuje. Następnie możesz zaimportować tylko te interfejsy, które w danym momencie Cię interesują.
— Dirk Dastardly,

@Drew, dziękuję za komentarz. Myślałem o użyciu MEF, ale chciałem sprawdzić, czy jest inne, tańsze rozwiązanie.
— M4N,

Nadanie fabryce klas wtyczek dobrze znanej nazwy, aby można było po prostu użyć bezpośrednio Activator.CreateInstance () jest prostym obejściem. Niemniej jednak, jeśli otrzymasz ten wyjątek teraz z powodu problemu z rozwiązywaniem asemblera, prawdopodobnie otrzymasz go również później.
— Hans Passant,

1
@Hans: Nie jestem pewien, czy całkowicie rozumiem. Zespół, który skanuję, może zawierać dowolną liczbę typów implementujących dany interfejs, więc nie ma jednego dobrze znanego typu. (a także:
— skanuję

2
Mam prawie ten sam kod i ten sam problem. Zespół, który eksploruję, składa się z AppDomain.CurrentDomain.GetAssemblies()tego, że działa na moim komputerze, ale nie na innych komputerach. Dlaczego do cholery niektóre zestawy z mojego pliku wykonywalnego i tak nie mogą być odczytywane / ładowane?
— v.oddou

Odpowiedzi:


130

Jednym dość nieprzyjemnym sposobem byłoby:

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

To zdecydowanie denerwujące, że trzeba to robić. Możesz użyć metody rozszerzenia, aby była ładniejsza w kodzie „klienta”:

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);
    }
}

Możesz chcieć returnusunąć instrukcję z bloku catch - ja sam nie bardzo chcę, żeby tam było, ale prawdopodobnie jest to najkrótszy kod ...


2
Dzięki, to wydaje się być rozwiązaniem (i zgadzam się, nie wydaje się być czystym rozwiązaniem).
— M4N,

4
To rozwiązanie nadal powoduje problemy podczas próby użycia listy typów ujawnionych w wyjątku. Bez względu na przyczynę wyjątku ładowania typu, FileNotFound, BadImage itp. Nadal będą rzucać przy każdym dostępie do typów, których dotyczy problem.
— sweetfa

@sweetfa: Tak, jest bardzo ograniczone - ale jeśli OP musi tylko znaleźć nazwy, na przykład, powinno być w porządku.
— Jon Skeet,

1
Zabawne, ten post jest tutaj cytowany, całkiem interesujący: haacked.com/archive/2012/07/23/…
— anhoppe

@sweetfa Oto, co robię, aby uniknąć problemu z wyjątkiem FileNotFound w zwracanych typach: From t As Type In e.Types Where (t IsNot Nothing) AndAlso (t.TypeInitializer IsNot Nothing)Wydaje się, że działa świetnie.
— ElektroStudios

22

Chociaż wydaje się, że nic nie można zrobić bez otrzymania w pewnym momencie wyjątku ReflectionTypeLoadException, powyższe odpowiedzi są ograniczone, ponieważ każda próba wykorzystania typów dostarczonych z wyjątku nadal będzie powodować problem z pierwotnym problemem, który spowodował, że typ nie załadował się.

Aby rozwiązać ten problem, poniższy kod ogranicza typy do tych znajdujących się w zestawie i umożliwia predykatowi dalsze ograniczenie listy typów.

    /// <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;
    }

4

Czy rozważałeś Assembly.ReflectionOnlyLoad ? Biorąc pod uwagę to, co próbujesz zrobić, może to wystarczyć.


2
Tak, rozważałem to. Ale nie użyłem go, ponieważ w przeciwnym razie musiałbym ręcznie załadować wszelkie zależności. Kod nie byłby również wykonywalny z ReflectionOnlyLoad (zobacz sekcję Uwagi na stronie, do której jesteś podłączony).
— M4N

3

W moim przypadku ten sam problem był spowodowany obecnością niechcianych zestawów w folderze aplikacji. Spróbuj wyczyścić folder Bin i odbuduj aplikację.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.