Как предотвратить исключение ReflectionTypeLoadException при вызове Assembly.GetTypes ()

97

Я пытаюсь сканировать сборку на предмет типов, реализующих определенный интерфейс, используя код, подобный этому:

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(), например, если сборка содержит типы, ссылающиеся на сборку, которая в настоящее время недоступна.

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

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

M4N
источник
1
Возможно, это будет намного больше, чем то, что вы ищете, но MEF дает вам аналогичные функции. Просто отметьте каждый из ваших классов тегом [Export], который определяет интерфейс, который он реализует. Затем вы можете импортировать только те интерфейсы, которые вам интересны в данный момент.
Дирк Дастардли
@Drew, Спасибо за комментарий. Я думал об использовании MEF, но хотел посмотреть, есть ли другое, более дешевое решение.
M4N
Присвоение фабрике классов плагина хорошо известного имени, чтобы вы могли просто использовать Activator.CreateInstance () напрямую, - это простой обходной путь. Тем не менее, если вы получите это исключение сейчас из-за проблемы с разрешением сборки, вы, вероятно, получите его и позже.
Ханс Пассан
1
@Hans: Я не совсем понимаю. Сканируемая мной сборка может содержать любое количество типов, реализующих данный интерфейс, поэтому нет ни одного хорошо известного типа. (а также: я сканирую более одной сборки, а не только одну)
M4N
2
У меня почти такой же код и та же проблема. И сборка, которую я исследую, дает AppDomain.CurrentDomain.GetAssemblies(), она работает на моей машине, но не работает на других машинах. Какого черта некоторые сборки из моего исполняемого файла все равно не читаются / не загружаются ??
v.oddou 01

Ответы:

130

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

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
Спасибо, похоже, это решение (и я согласен, это не кажется чистым решением).
M4N
4
Это решение по-прежнему имеет проблемы при попытке использовать список типов, представленных в исключении. Какой бы ни была причина исключения загрузки типа, FileNotFound, BadImage и т. Д. Будут по-прежнему вызывать каждый доступ к рассматриваемым типам.
sweetfa
@sweetfa: Да, это очень ограничено, но если OP просто нужно найти, например, имена, все должно быть в порядке.
Джон Скит
1
Забавно, этот пост процитирован здесь, довольно интересный: haacked.com/archive/2012/07/23/…
anhoppe
@sweetfa Это то, что я делаю, чтобы избежать проблемы исключения FileNotFound для возвращаемых типов: From t As Type In e.Types Where (t IsNot Nothing) AndAlso (t.TypeInitializer IsNot Nothing)похоже, он отлично работает.
ElektroStudios
22

Хотя кажется, что ничего нельзя сделать без получения исключения 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;
    }
Sweetfa
источник
4

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

Себ
источник
2
Да, я думал об этом. Но я не использовал его, потому что в противном случае мне пришлось бы вручную загружать любые зависимости. Также код не будет исполняться с ReflectionOnlyLoad (см. Раздел «Примечания» на странице, на которую вы ссылаетесь).
M4N
3

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

Сергей
источник