如何防止调用 Assembly. GetTypes()时发生 RefectionTypeLoadException

我试图扫描一个程序集来实现一个特定接口的类型,使用的代码类似于下面的代码:

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

我的问题是,在某些情况下,当调用 asm.GetTypes()时,我会得到一个 ReflectionTypeLoadException,例如,如果程序集包含引用当前不可用的程序集的类型。

就我而言,我对引起问题的类型不感兴趣。我正在搜索的类型不需要不可用的程序集。

问题是: 是否有可能以某种方式跳过/忽略导致异常但仍然处理程序集中包含的其他类型的类型?

42883 次浏览

One fairly nasty way would be:

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

It's definitely annoying to have to do this though. You could use an extension method to make it nicer in the "client" code:

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

You may well wish to move the return statement out of the catch block - I'm not terribly keen on it being there myself, but it probably is the shortest code...

Have you considered Assembly.ReflectionOnlyLoad ? Considering what you're trying to do, it might be enough.

Whilst it appears that nothing can be done without receiving the ReflectionTypeLoadException at some point, the answers above are limited in that any attempt to utilise the types provided from the exception will still give issue with the original issue that caused the type to fail to load.

To overcome this the following code limits the types to those located within the assembly and allows a predicate to further restrict the list of types.

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

In my case, the same problem was caused by the presence of unwanted assemblies in the application folder. Try to clear the Bin folder and rebuild the application.

The answer from Jon Skeet works fine, but you still get this exception thrown in your face every time. To work around that use the following snippet and turn on "Just My Code" in the debugging settings of Visual Studio.

[DebuggerNonUserCode]
public static IEnumerable<Type> GetSuccesfullyLoadedTypes(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
// If some types can't be loaded, this exception is thrown.
// The [DebuggerNonUserCode] makes sure these exceptions are not thrown in the developers
// face when they have "Just My Code" turned on in their debugging settings.
return e.Types.Where(t => t != null);
}
}