Enumerate classes by interface or inheritance using reflection for .NET 8+
$ dotnet add package MMPClassEnumeratorA simple utility for discovering and instantiating classes by interface or inheritance using reflection. Perfect for plugin systems, dynamic class loading, and educational purposes.
MMPClassEnumerator eliminates boilerplate code when working with plugins or dynamic class discovery. Instead of manually instantiating each class with switch statements, discover and create instances automatically using reflection.
Install-Package MMPClassEnumerator
dotnet add package MMPClassEnumerator
<PackageReference Include="MMPClassEnumerator" Version="1.2.0" />
Before - Manual instantiation with switch statements:
ITopic topic;
switch(choice)
{
case 0: topic = new Topic0(); break;
case 1: topic = new Topic1(); break;
case 2: topic = new Topic2(); break;
// Long list of cases...
}
After - Dynamic discovery and instantiation:
using MarcusMedinaPro.ClassEnumerator;
// Get all classes implementing ITopic
var topics = EnumerateClasses<ITopic>.GetClassesByInterface().ToList();
// Display options
for (int i = 0; i < topics.Count; i++)
Console.WriteLine($"{i:00} {topics[i].GetType().Name}");
// User selects
Console.Write("Select a topic: ");
var choice = int.Parse(Console.ReadLine()!);
// Use selected instance directly
ITopic topic = topics[choice];
Returns instantiated objects of all classes implementing the specified interface:
// Basic usage
IEnumerable<T> instances = EnumerateClasses<IMyInterface>.GetClassesByInterface();
// With caching for better performance
IEnumerable<T> cached = EnumerateClasses<IMyInterface>.GetClassesByInterface(useCache: true);
// Scan specific assembly
var assembly = Assembly.Load("MyPlugins");
IEnumerable<T> plugins = EnumerateClasses<IMyInterface>.GetClassesByInterface(assembly: assembly);
Returns instantiated objects of all classes inheriting from the specified base class:
IEnumerable<T> instances = EnumerateClasses<MyBaseClass>.GetClassesByInheritance();
Returns type definitions without creating instances:
IEnumerable<Type> types = EnumerateClasses<IMyInterface>.ListClassesByInterface();
Returns type definitions without creating instances:
IEnumerable<Type> types = EnumerateClasses<MyBaseClass>.ListClassesByInheritance();
public interface IPlugin
{
string Name { get; }
void Execute();
}
// Auto-discover and load all plugins
var plugins = EnumerateClasses<IPlugin>.GetClassesByInterface().ToList();
// Display available plugins
plugins.ForEach(p => Console.WriteLine($"Plugin: {p.Name}"));
// Execute all plugins
plugins.ForEach(p => p.Execute());
// Get all classes (using object as base)
var allClasses = EnumerateClasses<object>.GetClassesByInheritance().ToList();
// Display class names
allClasses.ForEach(c => Console.WriteLine($"class {c.GetType().Name}()"));
public interface IMenuItem
{
string DisplayName { get; }
void Run();
}
var menuItems = EnumerateClasses<IMenuItem>.GetClassesByInterface().ToList();
// Display menu
for (int i = 0; i < menuItems.Count; i++)
Console.WriteLine($"{i + 1}. {menuItems[i].DisplayName}");
// Execute selected item
int selection = int.Parse(Console.ReadLine()!) - 1;
menuItems[selection].Run();
MMPClassEnumerator uses .NET reflection to:
Activator.CreateInstance()This library was created as an educational tool to demonstrate:
// First call - scans assembly (slower)
var plugins = EnumerateClasses<IPlugin>.GetClassesByInterface(useCache: true);
// Subsequent calls - uses cache (much faster!)
var pluginsAgain = EnumerateClasses<IPlugin>.GetClassesByInterface(useCache: true);
// Clear cache if needed (e.g., after dynamic assembly loading)
EnumerateClasses<IPlugin>.ClearCache();
var allPlugins = new List<IPlugin>();
// Scan main assembly
allPlugins.AddRange(EnumerateClasses<IPlugin>.GetClassesByInterface());
// Scan plugin assemblies
var pluginAssemblies = Directory.GetFiles("./plugins", "*.dll");
foreach (var dll in pluginAssemblies)
{
var assembly = Assembly.LoadFrom(dll);
allPlugins.AddRange(EnumerateClasses<IPlugin>.GetClassesByInterface(assembly: assembly));
}
| Method | Items | Time | Speedup |
|---|---|---|---|
| No Cache | 10 classes | ~2ms | 1x |
| With Cache | 10 classes | ~0.01ms | 200x faster |
Caching is especially beneficial when called frequently (e.g., in request handlers)
New Features (backward compatible):
useCache parameter for all methods (default: false)assembly parameter to scan custom assembliesClearCache() method for cache managementImprovements:
?. operators)No breaking changes - existing code works as-is!
This work is licensed under the MIT License.
Full source available on GitHub
Made with ❤️ for educational purposes