Home > General > Loading types at runtime

Loading types at runtime

I have a library that contains a factory, which creates instances of a type that implement an interface. Currently the factory, interface, and the implementations are in the same assembly. For maintainability, I’d like to separate the implementations from the core library and put them into their own assemblies. This will allow me to version each implementation independently from the core library. Any applications that need to use certain implementations will simply need to reference the core library and any number of libraries that contain the implementations. The factory in the core library should be able to determine which implementations are available at runtime.

Here I have a class library Demo.Core that contains an interface definition and a factory. There are no implementations for IPrinter in the core library. I have two additional class libraries called Demo.GreenPrinter and Demo.RedPrinter that contains implementations for IPrinter. The two libraries that contain the implementations references the core library, but the core library does not reference either of the libraries that contain the implementations. The console application needs to references the core library and at least one of the additional libraries.

The interface contains two methods. CanHandle contains logic to determine if a printer should be used and the Print method prints the message to the screen.

public interface IPrinter
{
    bool CanHandle(string color);
    void Print(string message); 
}

The managed extensibility framework (MEF) makes it easy for me to accomplish what I’m trying to do. The factory contains a static list of IPrinters and is populated by the code in the static constructor using MEF. Unfortunately, MEF does not support static imports. I did not use MEF to automatically compose the PrinterFactory because I think it’s unnecessary to new up instances of each printer when only one is going to be returned, hence the static list and constructor.

public class PrinterFactory
{
    private static IList<IPrinter> Printers { get; set; }

    static PrinterFactory()
    {
        var catalog = new DirectoryCatalog(Environment.CurrentDirectory, "Demo.*");
        var container = new CompositionContainer(catalog);

        Printers = container.GetExportedValues<IPrinter>().ToList();
    }
        
    public IPrinter Create(string color)
    {
        foreach (IPrinter printer in Printers)
        {
            if (printer.CanHandle(color))
                return printer;
        }

        return null;
    }
}

I’m using the DirectoryCatalog to find the assemblies. The core library and the libraries that contain the implementations will normally be copied to the same bin directory since they are both referenced by the console application. My console application can now create a PrinterFactory to create an IPrinter.

static void Main(string[] args)
{
	var factory = new PrinterFactory();
	RedPrinter printer = factory.Create("red") as RedPrinter;

	printer.Print("hello world");
}

This is all I would need to do if my core library was targeting .NET 4.0. However, if my core library needs to target an older version of the framework (2.0, 3.0, 3.5), I cannot use MEF without adding an extra dependency. At this point, I need to fall back to loading assemblies and using the Activator to create an instance of each IPrinter. The factory’s static constructor now looks like:

static PrinterFactory()
{
	string[] paths = Directory.GetFiles(Environment.CurrentDirectory, "Demo.*.dll");

	foreach (string path in paths)
		Assembly.LoadFrom(path);

	Printers = AppDomain.CurrentDomain
		.GetAssemblies()
		.SelectMany(x => x.GetTypes())
		.Where(x => typeof(IPrinter).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface)
		.Select(x => (IPrinter)Activator.CreateInstance(x))
		.ToList();
}
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: