I've been trying to find all classes that implement a certain interface. I have never worked with any kind of reflection before so I am kinda new to this. With that said I've implemented several approaches based on a bunch of articles and stack overflow posts but still couldn't get it to work.
The problem is that I can't seem to find the class that implements an interface across multiple assemblies in .NET6.
To give you a bit of context here is my current test implementation that sums up the problem pretty good.
Note: In case references between assemblies matter, AssemblyA -> AssemblyB, AssemblyC -> AssemblyB
//Assembly A
public class SomeFunnyClass
{
public static void Main(string[] args)
{
Assembly.LoadFrom("AssemblyB.dll");
Assembly assemblyC = Assembly.LoadFrom("AssemblyC.dll");
SomeTests(assemblyC);
}
public void SomeTests(Assembly assembly)
{
var test = assembly.GetType("TestLoading").GetInterfaces(); // ILoading
var test2 = typeof(ILoading).IsAssignableFrom(assembly.GetType("TestLoading")); // false
}
}
Here is already my issue, in my first test I get the name of the interface ILoading
which is correct.
But then with the 2nd test case, I get false
which I would expect to be true
.
// Assembly B
public interface ILoading
{
void OnLoad();
}
// Assembly C
public class TestLoading : ILoading
{
public void OnLoad(){
// Do some cool stuff here
}
}
I've already tried several different approaches but always end up with the same result. I also made sure that all the required Assemblies are loaded.
Does anyone have an idea on what why the TestLoading
class is not found?
You need to load current assembly, then get its location, and name. Then, you search the directory (from the given location) for all .dll
and filter the search with files that starts with the assembly name (that if your all types are under the same assembly with different namespaces such as (Project, Project.Core, Project.Something ..etc.).
Here is a working code that I've got from one of my old projects, it will help you to understand the logic. The class will only load assemblies that started with the current assembly name, and cache the types, so it can be reused without the need to reload the assemblies again.
public static class TypeLoader
{
private static Type[] _loadedTypes;
private static IEnumerable<Type> GetLoadedTypes()
{
var assembly = Assembly.GetExecutingAssembly();
var location = assembly.Location;
var folder = location[..location.LastIndexOf('\\')];
var assemblyName = assembly.GetName().Name;
var index = assemblyName.IndexOf('.');
var offset = index == -1 ? assemblyName.Length : index;
var searchPattern = string.Format("{0}*.dll", assemblyName.Substring(0, offset));
foreach (var file in Directory.GetFiles(folder, searchPattern))
{
foreach (var type in Assembly.LoadFrom(file).GetTypes())
{
yield return type;
}
}
}
public static IEnumerable<T> GetInstances<T>() where T : class
{
if(_loadedTypes == null)
_loadedTypes = GetLoadedTypes().ToArray();
foreach (var type in _loadedTypes.Where(x => (x.IsSubclassOf(typeof(T)) || typeof(T).IsAssignableFrom(x)) && x.IsClass && !x.IsAbstract))
{
yield return Activator.CreateInstance(type) as T;
}
}
}
be advised, this type of work should be avoid if you're using .NET5+, instead, you should use DI instead.