Reputation: 60674
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
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
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
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
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