I'm trying to scan an assembly for types implementing a specific interface using code similar to this:
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;
}
My problem is, that I get a ReflectionTypeLoadException
when calling asm.GetTypes()
in some cases, e.g. if the assembly contains types referencing an assembly which is currently not available.
In my case, I'm not interested in the types which cause the problem. The types I'm searching for do not need the non-available assemblies.
The question is: is it possible to somehow skip/ignore the types which cause the exception but still process the other types contained in the assembly?
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...
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With