MMPClassEnumerator 1.2.0

dotnet add package MMPClassEnumerator --version 1.2.0
                    
NuGet\Install-Package MMPClassEnumerator -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MMPClassEnumerator" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MMPClassEnumerator" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="MMPClassEnumerator" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MMPClassEnumerator --version 1.2.0
                    
#r "nuget: MMPClassEnumerator, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MMPClassEnumerator@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MMPClassEnumerator&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=MMPClassEnumerator&version=1.2.0
                    
Install as a Cake Tool

MMPClassEnumerator

NuGet .NET License

A simple utility for discovering and instantiating classes by interface or inheritance using reflection. Perfect for plugin systems, dynamic class loading, and educational purposes.

Overview

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.

Features

  • Interface-based Discovery - Find classes implementing specific interfaces
  • Inheritance-based Discovery - Find classes inheriting from base classes
  • Instance Creation - Auto-instantiate discovered classes
  • Type Listing - Get type definitions without instantiation
  • Performance Caching - Optional caching for improved performance
  • Custom Assembly Scanning - Scan any assembly, not just calling assembly
  • Comprehensive Tests - Full test coverage with xUnit
  • Safe Exception Handling - Gracefully handles instantiation failures
  • Zero Dependencies - Pure .NET reflection
  • Educational Tool - Great for teaching reflection and plugins

Requirements

  • .NET 8.0 or higher

Installation

Package Manager Console

Install-Package MMPClassEnumerator

.NET CLI

dotnet add package MMPClassEnumerator

Package Reference

<PackageReference Include="MMPClassEnumerator" Version="1.2.0" />

Quick Start

The Problem

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...
}

The Solution

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];

API Reference

Get Instances by Interface

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);

Get Instances by Inheritance

Returns instantiated objects of all classes inheriting from the specified base class:

IEnumerable<T> instances = EnumerateClasses<MyBaseClass>.GetClassesByInheritance();

List Types by Interface

Returns type definitions without creating instances:

IEnumerable<Type> types = EnumerateClasses<IMyInterface>.ListClassesByInterface();

List Types by Inheritance

Returns type definitions without creating instances:

IEnumerable<Type> types = EnumerateClasses<MyBaseClass>.ListClassesByInheritance();

Usage Examples

Plugin System

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());

List All Classes in Assembly

// 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();

How It Works

MMPClassEnumerator uses .NET reflection to:

  1. Scan the calling assembly for types
  2. Filter types matching the specified interface or base class
  3. Verify types have parameterless constructors
  4. Optionally create instances via Activator.CreateInstance()
  5. Return results sorted by type name

Requirements and Limitations

  • Parameterless Constructor: All discovered classes must have a public parameterless constructor
  • Calling Assembly: Only scans the assembly that calls the method
  • Performance: Reflection has overhead - cache results if calling frequently

Educational Purpose

This library was created as an educational tool to demonstrate:

  • Reflection API: How to discover types at runtime
  • Generic Programming: Using generic type parameters
  • LINQ: Query syntax for filtering types
  • NuGet Publishing: Complete package creation workflow

Performance - With Caching

// 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();

Advanced - Scan Multiple Assemblies

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));
}

Performance Benchmarks

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)

Migration Guide

From v1.0.x to v1.1.0

  • .NET 6 → .NET 8: Update project target framework
  • Improved Descriptions: Better package metadata
  • XML Documentation: Full API documentation
  • No breaking API changes - drop-in replacement

From v1.1.0 to v1.2.0

New Features (backward compatible):

  • useCache parameter for all methods (default: false)
  • assembly parameter to scan custom assemblies
  • ClearCache() method for cache management
  • Comprehensive test suite included

Improvements:

  • Better null-safety (removed excessive ?. operators)
  • Improved exception handling
  • Excludes abstract classes and interfaces automatically
  • Better XML documentation with code examples

No breaking changes - existing code works as-is!

License

This work is licensed under the MIT License.

Credits

Source Code

Full source available on GitHub


Made with ❤️ for educational purposes

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 187 10/8/2025
1.1.0 182 10/8/2025
1.0.0.24 647 1/11/2022
1.0.0.23 417 1/5/2022