Як запобігти 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()в деяких випадках, наприклад, якщо збірка містить типи, що посилаються на збірку, яка наразі недоступна.

У моєму випадку мене не цікавлять типи, що викликають проблему. Типи, які я шукаю, не потребують недоступних збірок.

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


1
Це може бути набагато більше переписування, ніж те, що ви шукаєте, але MEF надає вам подібну функціональність. Просто позначте кожен із своїх класів тегом [Export], який визначає інтерфейс, який він реалізує. Тоді ви можете імпортувати лише ті інтерфейси, які вас цікавлять на той момент.
Dirk Dastardly

@Drew, Дякую за ваш коментар. Я думав про використання MEF, але хотів подивитися, чи є інше, більш дешеве рішення.
M4N

Надання фабриці класів плагінів відомого імені, щоб ви могли просто використовувати Activator.CreateInstance () - це просте обхідне рішення. Тим не менше, якщо ви отримаєте цей виняток зараз через проблему з роздільною здатністю збірки, ви, ймовірно, отримаєте його і пізніше.
Ганс Пассант,

1
@Hans: Я не впевнений, що повністю розумію. Збірка, яку я сканую, може містити будь-яку кількість типів, що реалізують даний інтерфейс, тому немає жодного відомого типу. (а також: я
сканую

2
У мене майже той самий код і та сама проблема. І збірка, яку я досліджую, дана AppDomain.CurrentDomain.GetAssemblies(), це працює на моїй машині, але не на інших машинах. Чому, блін, деякі збірки з мого виконуваного файлу в будь-якому разі не будуть читабельними / завантаженими ??
v.oddou

Відповіді:


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: Так, це дуже обмежено - але якщо ОП просто потрібно знайти імена, наприклад, це повинно бути нормально.
Джон Скіт,

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

4

Ви розглядали Assembly.ReflectionOnlyLoad ? Враховуючи те, що ви намагаєтесь зробити, цього може бути достатньо.


2
Так, я це вважав. Але я не використовував його, бо інакше мені довелося б вручну завантажувати будь-які залежності. Також код не може бути виконуваним за допомогою ReflectionOnlyLoad (див. Розділ "Зауваження" на сторінці, на яку ви посилаєтесь).
M4N

3

У моєму випадку та ж проблема була спричинена наявністю небажаних збірок у папці програми. Спробуйте очистити папку Bin та відновити додаток.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.