Unfortunately, and surprisingly the Assembly.ReflectionOnlyLoadFrom(string filename) method also locks files, which was a surprise.... but Jeffry Richter explains the reasoning behind this in his book "CLR via C#". The reason is that the loaded assembly may contain types which derive from types in a different assembly..... So I had to fall back on the strategy of loading the assembly into a temporary application domain, reflect on the assembly and unload the application domain thus unlocking the file and unloading it from memory.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace AssemblyDiscoveryConsole
{
// The attribute class you are looking for within external assemblies
public class SomeAttribute : Attribute
{
}
public class TypeFinder
{
public string[] LoadTypes(string assemblyFilename)
{
AppDomain appDomain = AppDomain.CreateDomain("TypeFinder");
try
{
// Load and query asseblies in domain and return results
AssemblyReflector ar = (AssemblyReflector)appDomain.CreateInstanceAndUnwrap(
typeof(AssemblyReflector).Assembly.FullName,
typeof(AssemblyReflector).FullName);
return ar.LoadTypes(assemblyFilename);
}
finally
{
// unload the temporary domain
AppDomain.Unload(appDomain);
}
}
/// <summary>
/// Class used to load and query assemblies within "TypeFinder" Domain
/// </summary>
private class AssemblyReflector : MarshalByRefObject
{
private IEnumerable<string> EnumerateTypes(string assemblyFilename)
{
Assembly asm = Assembly.LoadFile(assemblyFilename);
if (asm == null) throw new Exception("Assembly not found");
foreach (Type t in asm.GetTypes())
{
if (t.GetCustomAttributes(typeof(SomeAttribute), true).FirstOrDefault() != null)
{
yield return t.AssemblyQualifiedName;
}
}
}
public string[] LoadTypes(string assemblyFilename)
{
return EnumerateTypes(assemblyFilename).ToArray();
}
}
}
}