Sample console program.
class Program
{
static void Main(string[] args)
{
// ... code to build dll ... not written yet ...
Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
// don't know what or how to cast here
// looking for a better way to do next 3 lines
IRunnable r = assembly.CreateInstance("TestRunner");
if (r == null) throw new Exception("broke");
r.Run();
}
}
I want to dynamically build an assembly (.dll), and then load the assembly, instantiate a class, and call the Run() method of that class. Should I try casting the TestRunner class to something? Not sure how the types in one assembly (dynamic code) would know about my types in my (static assembly / shell app). Is it better to just use a few lines of reflection code to call Run() on just an object? What should that code look like?
UPDATE: William Edmondson - see comment
I'm doing exactly what you're looking for in my rules engine, which uses CS-Script for dynamically compiling, loading, and running C#. It should be easily translatable into what you're looking for, and I'll give an example. First, the code (stripped-down):
This will take an interface of type T, compile a .cs file into an assembly, instantiate a class of a given type, and align that instantiated class to the T interface. Basically, you just have to make sure the instantiated class implements that interface. I use properties to setup and access everything, like so:
For your example, you want to call Run(), so I'd make an interface that defines the Run() method, like this:
Then make a class that implements it, like this:
Change the name of RulesEngine to something like TestHarness, and set your properties:
Then, anywhere you want to call it, you can just run:
It would probably work great for a plugin system, but my code as-is is limited to loading and running one file, since all of our rules are in one C# source file. I would think it'd be pretty easy to modify it to just pass in the type/source file for each one you wanted to run, though. You'd just have to move the code from the getter into a method that took those two parameters.
Also, use your IRunnable in place of ITestRunner.
Use an AppDomain
It is safer and more flexible to load the assembly into its own
AppDomain
first.So instead of the answer given previously:
I would suggested the following (adapted from this answer to a related question):
Now you can unload the assembly and have different security settings.
If you want even more flexibility and power for dynamic loading and unloading of assemblies you should look at the Managed Add-ins Framework (i.e. the
System.AddIn
namespace). For more information see this article on Add-ins and Extensibility on MSDN.If you do not have access to the
TestRunner
type information in the calling assembly (it sounds like you may not), you can call the method like this:If you have access to the
IRunnable
interface type, you can cast your instance to that (rather than theTestRunner
type, which is implemented in the dynamically created or loaded assembly, right?):You will need to use reflection to get the type "TestRunner". Use the Assembly.GetType method.
When you build your assembly, you can call
AssemblyBuilder.SetEntryPoint
, and then get it back from theAssembly.EntryPoint
property to invoke it.Keep in mind you'll want to use this signature, and note that it doesn't have to be named
Main
: