views:

135

answers:

3

I have a scenario whereby I want n amount of classes to look at the same data and decide if any work needs to be done. Work is done by a team, and multiple teams can work on the data at the same time. I was thinking of creating a class for every team that would implement the CreateWork interface. All CreateWork classes must have their say. At the moment there are only a few but in the future there will be many more.

Sudo code for my planned solution

For each CreateWork class in assembly
    class.CheckAndCreateWork(dataIn,returnedCollectionOfWorkToBeDone)
Next

Is there a design pattern that can accomplish this in an elegant way? Seems a bit messy to loop round every class in the assembly.

Cheers

+9  A: 

You can do the following:

  1. Get all the types defined in the assembly by using Assembly.GetTypes. To obtain a reference for the asembly your code is running on, use Assembly.GetExecutingAssembly.

  2. For each type, check if it implements the desired interface by using Type.IsAssignableFrom.

  3. For each appropriate type, create a instance by using Activator.CreateInstance.

It would be approximately like this (not tested):

foreach(var type in Assembly.GetExecutingAssembly().GetTypes()) {
    if(typeof(ITheInterface).IsAssignableFrom(type)) {
        var theInstance=(ITheInterface)Activator.CreateInstance(type);
        //do something with theInstance
    }
}
Konamiman
+1. I *believe* that `IsAssignableFrom` is slower then `GetInterfaces().Contains(type)` (which only works for interfaces). But I might be wrong.
Stefan Steinegger
I generally prefer IsAssignableFrom because it covers a broad scope than just interface implementations, for example it can tell you if class A inherits from class B.
Konamiman
IsAssignableFrom is the way to go, GetInterfaces returns the interfaces defined on the current type only, but that doesn't check interface inheritance (IBar : IFoo). csc simplifies that by emitting all the interface chain on the type, but not all compilers do that.
Jb Evain
+1 --> awesome solution
JohnIdol
A: 

It's a standard plugin task. There are many implementations out there. If you have already loaded your assemlby, finding types you are interested in is easy - you need to use method assemlby.GetExportedTypes() and Activator.CreateInstance() (provided you have parameterless constructor - otherwise you may wish to use container to inject dependencies and build up your worker instances)

For simple case this is sufficient:

var workerTypes = assembly.GetExportedTypes()
    .Where(t => t.IsClass && !t.IsAbstract && typeof(IWorker).IsAssignableFrom(t));

foreach (var type in workerTypes)
{
    var worker = (IWorker)Activator.CreateInstance(type);
    worker.CheckAndCreateWork("Work");
}

Getting and loading assemlby is another topic. You can either load in run-time using Assemlby.LoadFrom() or link them statically to your application and use something like this:

var assembly = typeof(SomeClassInTargetAssembly).Assembly;

Dynamic loading may be done by enumerating through all the *.dll files in a specific directory (for example, ./plugins) like this:

foreach (var file in Directory.GetFiles(PluginsFolder, "*.dll"))
{
    var assembly = Assembly.LoadFrom(file);
    var workerTypes = GetWorkerTypes(assembly);
    RunWorkers(workerTypes);
}

You may want to separate processes of loading and running workers to avoid loading worker types multiple times (in case you need to run workers more than once during application life time)

Konstantin Spirin
A: 

I think you should use the decorator pattern.

Auto Allocation Boy