You can use reflection to get all classes in the current assembly that implement a specific interface. Here’s how:
private IEnumerable<Type> GetAllTypesThatImplementInterface<T>()
{
return System.Reflection.Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
}
Code language: C# (cs)
To create instances of these types, loop through them and use Activator.CreateInstance(), like so:
foreach (var type in GetAllTypesThatImplementInterface<T>())
{
var instance = (T)Activator.CreateInstance(type);
//do something with instance
}
Code language: C# (cs)
Example – Auto-wire a command routing table
Let’s say we want to build a command routing table. We have commands and want to automatically wire up the command handlers.
This is similar to how web APIs work. You specify which route a controller handles. When a request comes in, the web framework automatically calls the controller that handles that route.
Create the Command Handler interface
public interface ICommandHandler
{
string HandlesCommand { get; }
void Handle(string command, string data);
}
Code language: C# (cs)
Create the command routing table
This loads all types that implement the ICommandHandler interface. Then it adds them to a dictionary, resulting in commands mapped to command handler objects.
public class CommandRoutingTableBuilder
{
public Dictionary<string, ICommandHandler> GetCommandRoutingTable()
{
var commandRoutingTable = new Dictionary<string, ICommandHandler>();
foreach (var type in GetAllTypesThatImplementInterface<ICommandHandler>())
{
var handler = (ICommandHandler)Activator.CreateInstance(type);
commandRoutingTable.Add(handler.HandlesCommand, handler);
}
return commandRoutingTable;
}
private IEnumerable<Type> GetAllTypesThatImplementInterface<T>()
{
return System.Reflection.Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(T).IsAssignableFrom(type) && !type.IsInterface);
}
}
Code language: C# (cs)
Implement a command handler
To show this working we’ll create a simple command handler. It handles the “repeat” command by writing the data twice to the console.
public class RepeatDataCommandHandler : ICommandHandler
{
public string HandlesCommand => "repeat";
public void Handle(string command, string data)
{
Console.WriteLine($"I repeat: {data} {data}");
}
}
Code language: C# (cs)
Example of using the routing table to automatically handle a command
Now when a command comes in, you can lookup the value in the dictionary:
static void Main(string[] args)
{
var commandRoutingTable = new CommandRoutingTableBuilder().GetCommandRoutingTable();
string command = "repeat";
string data = "hello world";
commandRoutingTable[command].Handle(command, data);
}
Code language: C# (cs)
Because RepeatDataCommandHandler is mapped to the “repeat” command, this code calls RepeatDataCommandHandler.Handle(), which outputs “I repeat: hello world hello world” to the console.