Correct Way to Load Assembly, Find Class and Call Run Method
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Loading an assembly at runtime and calling a Run() method is a common plugin pattern in .NET. The reliable solution is to separate two concerns: how the assembly is loaded and how the target type is discovered and invoked.
Prefer a Shared Contract Over Raw Reflection
If you control both sides of the system, define a shared interface in a small contract assembly. That removes most of the brittle string-based reflection logic.
A plugin can implement that interface:
Then the host loads the plugin assembly, finds the concrete type, creates it, and calls Run() through the interface:
This approach gives you compile-time checking for the method signature and avoids typos in class names or method names.
When You Must Use Reflection by Name
Sometimes you do not have a shared contract and must load a specific type by its full name. In that case, be explicit about the type name, constructor requirements, and method signature.
The important part is the explicit method lookup. GetMethod("Run") by itself can return the wrong overload if multiple methods share that name.
Loading Dependencies Correctly
Loading the main assembly is often not the hard part. The real failure happens when the plugin has its own private dependencies in the same folder. In modern .NET, use a custom AssemblyLoadContext with AssemblyDependencyResolver when plugins need isolated dependency resolution.
Usage:
If your plugin references packages that are not already loaded by the host, this extra step can be the difference between a working loader and a FileNotFoundException.
Choosing Between Assembly.Load and LoadFromAssemblyPath
Use the API that matches your input:
- If you have an assembly name,
Assembly.Loadis appropriate. - If you have a file path, use
LoadFromAssemblyPath.
Passing a file path into the wrong API is a common source of confusion. It can work differently than expected because .NET resolves names and paths through different mechanisms.
For plugin scenarios, a file-path-based load is usually the clearer and safer choice.
Make Failure Modes Obvious
Runtime loading code is much easier to debug when each failure has a specific message. Do not let everything collapse into a generic reflection exception.
Good loader code should fail clearly when:
- the file path is wrong,
- the type cannot be found,
- the type is abstract or lacks a public constructor,
- the
Run()method is missing or has the wrong signature, - the plugin's dependencies cannot be resolved.
That makes support work dramatically easier once multiple plugins exist.
Common Pitfalls
- Using
Assembly.Loadwith a file path instead of a real assembly name. - Searching for a type by short name when the assembly contains more than one matching class.
- Calling
GetMethod("Run")without checking the parameter list or overloads. - Assuming plugin dependencies will resolve automatically without a custom load context.
- Building a reflection-only design when a shared interface would remove most of the fragility.
Summary
- The best design is a shared contract such as
IRunnableplus runtime assembly loading. - If you must use reflection, look up the exact type and exact
Run()signature. - '
LoadFromAssemblyPathis the right API when you are starting from a plugin file path.' - Plugin dependencies often require
AssemblyDependencyResolverand a customAssemblyLoadContext. - Clear error handling is essential because runtime loading failures are otherwise hard to diagnose.

