Tomas Aschan
Tomas Aschan

Reputation: 60674

Instantiate all classes implementing a specific interface

I have an interface IExample, and a set of classes ClassOne, ClassTwo and ClassThree, all defined in different namespaces. I will possibly remove either of the classes, or add a new one in a new place, at a later stage in development.

Now, I want to find all types that implement IExample at runtime, and instantiate them. (I know on beforehand that no class implementing IExample will ever need any constructor arguments, but I don't know how to specify that in code, so it's me - not the compiler - that knows...)

Is this possible? How do I go about to do it?

Update: I've now tried several of the approaches suggested, but on all of them, the line Activator.CreateInstance(type), I get a System.MissingMethodException because I "cannot create an instance of an interface." This is my code:

var tasks = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(a => a.GetTypes())
    .Where(t => typeof(IBootstrapperTask).IsAssignableFrom(t))

    // This line is where it fails
    .Select(t => Activator.CreateInstance(t) as IBootstrapperTask)

    .ToArray();
new AutoMapperBootstrapper(tasks).Initialize();

Without the as clause I don't see any exception, but I'm given an object[], and I need an IBootstrapperTask[] for the constructor on the last line in the excerpt. I've tried various ways to cast it, but none seem to work.

Upvotes: 20

Views: 12105

Answers (4)

Christoph
Christoph

Reputation: 4401

Do you need this to happen dynamically? Are there ever times when you might have one that you don't want to create? Seems to me that this is a good case for dependency injection (which can be configured). For example Unity has a ResolveAll method .

From the above link;

IEnumerable<IMyObject> objects = myContainer.ResolveAll<IMyObject>();

Upvotes: 5

Jon Skeet
Jon Skeet

Reputation: 1503839

You'd need to know a list of assemblies to look in, but then LINQ makes it relatively easy:

var instances = (from assembly in assemblies
                 from type in assembly
                 where !type.IsAbstract && 
                       type.IsClass &&
                       type.IsPublic &&
                       !type.IsGenericType &&
                       typeof(IExample).IsAssignableFrom(type)
                 let ctor = type.GetConstructor(Type.EmptyTypes)
                 where ctor != null && ctor.IsPublic
                 select (IExample) Activator.CreateInstance(type))
                .ToList();

You may think of some other restrictions to add, but they're pretty easy to express :)

instances will then be a List<IExample>.

EDIT: I suspect my code will work where yours didn't, because I'm specifically excluding non-classes. My guess is that your code is trying to instantiate the interface itself, i.e. when t is typeof(IBootstrapperTask).

Upvotes: 8

JaredPar
JaredPar

Reputation: 755557

This can be done with Reflection. For example

var interfaceType = typeof(IExample);
var all = AppDomain.CurrentDomain.GetAssemblies()
  .SelectMany(x => x.GetTypes())
  .Where(x => interfaceType.IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
  .Select(x => Activator.CreateInstance(x));

Note: This will only create instances of IExample from assemblies loaded in the current AppDomain. This can be different than all assemblies loaded in the current process. However for many types of applications this will be equivalent.

Upvotes: 34

Aliostad
Aliostad

Reputation: 81700

Try using reflection to get the types implementing IExample. This example looks at the current assembly but you could easily pass a different assembly:

        IEnumerable<Type> types = Assembly.GetExecutingAssembly()
            .GetTypes().Where(t=>t.GetInterfaces().Where(tt==typeof(IExample)).Count()>0);
        List<object> objects = new List<object>();
        foreach (Type type in types)
        {
            objects.Add(Activator.CreateInstance(type));
        }

Upvotes: 0

Related Questions