Как предотвратить 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()
в некоторых случаях, например, если сборка содержит типы, ссылающиеся на сборку, которая в данный момент недоступна.
В моем случае меня не интересуют типы, вызывающие проблему. Типы, которые я ищу, не нуждаются в недоступных сборках.
Вопрос заключается в следующем: возможно ли каким-то образом пропустить / игнорировать типы, которые вызывают исключение, но все же обрабатывают другие типы, содержащиеся в сборке?
4 ответа
Один довольно неприятный способ будет:
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 - я сам не очень хочу быть там, но это, наверное , самый короткий код...
Хотя кажется, что в какой-то момент ничего нельзя сделать, не получив исключение 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;
}
Рассматривали ли вы Assembly.ReflectionOnlyLoad? Учитывая то, что вы пытаетесь сделать, этого может быть достаточно.
Ответ от Джона Скита работает нормально, но вы все равно каждый раз получаете это исключение. Чтобы обойти это, используйте следующий фрагмент и включите "Только мой код" в настройках отладки Visual Studio.
[DebuggerNonUserCode]
public static IEnumerable<Type> GetSuccesfullyLoadedTypes(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
// For unknown reasons, sometimes some types can't be loaded and this exception is thrown.
// The [DebuggerNonUserCode] makes sure these exceptions are not thrown in the developers
// face when he has "Just My Code" turned on in his debugging settings.
return e.Types.Where(t => t != null);
}
}
В моем случае такая же проблема была вызвана наличием нежелательных сборок в папке приложения. Попробуйте очистить папку Bin и пересобрать приложение.