Как предотвратить ReflectionTypeLoadException при вызове 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;
}

Моя проблема в том, что я получаю ReflectionTypeLoadException при вызове asm.GetTypes() в некоторых случаях, например. если сборка содержит типы, ссылающиеся на сборку, которая в настоящее время недоступна.

В моем случае меня не интересуют типы, которые вызывают проблему. Типы, которые я ищу, не нуждаются в недоступных ассемблерах.

Вопрос: возможно ли каким-то образом пропускать/игнорировать типы, вызывающие исключение, но все же обрабатывать другие типы, содержащиеся в сборке?

Ответ 1

Один довольно неприятный способ:

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

Это определенно раздражает, когда нужно это делать. Вы можете использовать метод расширения, чтобы сделать его более приятным в "клиентском" коде:

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

Возможно, вы захотите переместить оператор return из блока catch - я не очень увлечен тем, что он есть сам, но это, вероятно, самый короткий код...

Ответ 2

В то время как кажется, что ничего не может быть сделано без получения ReflectionTypeLoadException в какой-то момент, ответы, приведенные выше, ограничены тем, что любая попытка использовать типы, предоставленные из исключения, все равно даст проблему с исходной проблемой, которая вызвала ошибку типа для загрузки.

Чтобы преодолеть это, следующий код ограничивает типы теми, которые расположены в сборке, и позволяет предикату дополнительно ограничивать список типов.

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

Ответ 3

Вы считали Assembly.ReflectionOnlyLoad? Учитывая то, что вы пытаетесь сделать, этого может быть достаточно.

Ответ 4

В моем случае та же проблема была вызвана наличием нежелательных сборок в папке приложения. Попробуйте очистить папку Bin и перестроить приложение.