views:

6544

answers:

17

How can I do what's in the title, with the minimum amount of code, using whatever c# 3.5 syntax (I'm guessing lambda expressions would fit, but I still don't understand them fully)?

In short, I want to iterate through all types that implement a particular interface.

Edit: I wasn't clear. Let me re-phrase my question: How can I, using reflection, get all types that implement an interface with C# 3.5 with the least code, and minimizing iterations

This is what I want to re-write:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ;
A: 

You're looking for reflection. I'm not sure if lambda expressions would really help you there; but more information is needed in any case: are you looking to find all types that implement an interface within .NET? Or within a particular assembly?

DannySmurf
+3  A: 

loop through all loaded assemblies, loop through all their types, and check if they implement the interface.

something like:

Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
    foreach (Type t in asm.GetTypes())
    {
        if (ti.IsAssignableFrom(t))
        {
            // here's your type in t
        }
    }
}
Lasse V. Karlsen
A: 

The post I linked shows how to load a dll and the reflect over it.

This might not be the smallest implementation but it worked for me. Se: this post

elgrego
+34  A: 

min would be this in c# 3.5 :)

var type = typeof(IMyInteraface);
var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

Basically, the least amount of iterations will always be:

loop assemblies  
 loop types  
  see if implemented.
Darren Kopp
Any reason you have the `ToList()` part? The `SelectMany` extension works on any `IEnumerable<T>`.
troethom
no. i noticed that a little while back too but didn't bother to edit it.
Darren Kopp
Where(p => type.IsAssignableFrom(p)) can be written asWhere(type.IsAssignableFrom)
graffic
Note that the above code will find and include the interface itself. If you don't want that (which is likely), change the where to :.Where(p=>type.IsAssignableFrom(p)
Lee Oades
Come to think of it, if you want to only return classes and not other interfaces derived from the interface, then do this:Where(p=>type.IsAssignableFrom(p)
Lee Oades
+1  A: 

Edit: I've just seen the edit to clarify that the original question was for the reduction of iterations / code and that's all well and good as an exercise, but in real-world situations you're going to want the fastest implementation, regardless of how cool the underlying LINQ looks.

Here's my Utils method for iterating through the loaded types. It handles regular classes as well as interfaces, and the excludeSystemTypes option speeds things up hugely if you are looking for implementations in your own / third-party codebase.

public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes)
{
    List<Type> list = new List<Type>();
    IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
    while (enumerator.MoveNext())
    {
        try
        {
            Type[] types = ((Assembly) enumerator.Current).GetTypes();
            if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System.")))
            {
                IEnumerator enumerator2 = types.GetEnumerator();
                while (enumerator2.MoveNext())
                {
                    Type current = (Type) enumerator2.Current;
                    if (type.IsInterface)
                    {
                        if (current.GetInterface(type.FullName) != null)
                        {
                            list.Add(current);
                        }
                    }
                    else if (current.IsSubclassOf(type))
                    {
                        list.Add(current);
                    }
                }
            }
        }
        catch
        {
        }
    }
    return list;
}

It's not pretty, I'll admit.

tags2k
A: 

There's no easy way (in terms of performance) to do what you want to do.

Reflection works with assemblys and types mainly so you'll have to get all the types of the assembly and query them for the right interface. Here's an example:

Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);

That will get you all the types that implement the IMyInterface in the Assembly MyAssembly

Jorge Córdoba
A: 

Not wanting to fire up my Windows VM at the moment, would something like...

IEnumerable<IMyInterface> implements = this.GetType().Assembly.GetTypes().OfType<IMyInterface>();

work? It's a LINQ extension method, that filters an IEnumerable T into an IEnumerable T' of all the elements from the first that can be cast to T' (which sounds like what you want).

Edit: Ah, ignore this. Your micro-example code at the end of your question isn't actually the behaviour the body of your question asks for - see Judah Himango's answer.

Adam Wright
GetTypes() returns an array of Type objects, which don't implement IMyInterface.
Joel Mueller
A: 

You could use some LINQ to get the list:

var types = from type in this.GetType().Assembly.GetTypes()
            where type is ISomeInterface
            select type;

But really, is that more readable?

Ryan Rinaldi
It might be more readable, if it worked. Unfortunately, your where clause is checking to see if an instance of the System.Type class implements ISomeInterface, which will never be true, unless ISomeInterface is really IReflect or ICustomAttributeProvider, in which case it will always be true.
Joel Mueller
+3  A: 

To find all types in an assembly that implement IFoo interface:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;

Note that Ryan Rinaldi's suggestion was incorrect. It will return 0 types. You cannot write

where type is IFoo

because type is a System.Type instance, and will never be of type IFoo. Instead, you check to see if IFoo is assignable from the type. That will get your expected results.

Also, Adam Wright's suggestion, which is currently marked as the answer, is incorrect as well, and for the same reason. At runtime, you'll see 0 types come back, because all System.Type instances weren't IFoo implementors.

Judah Himango
A: 

@tags2k

your code would be faster if you did something like:

...

if (excludeSystemTypes && 
    (enumerator.Current as Assembly).FullName.StartsWith("System."))
continue;
Darren Kopp
A: 

This is a clear example of what StackOverflow is for... I did a search in google but couldn't find exactly what I wanted, and within minutes I got an answer here.

Thanks for all the responses, I wish I could accept more than one answer! =)

(EDIT: I did vote up quite few though)

Juan Manuel
A: 

@Adam Wright

That's not correct.

IEnumerable<IMyInterface> implements = this.GetType().Assembly
                                                     .GetTypes()
                                                     .OfType<IMyInterface>();

That would not return anything because the Type class (that OfType<> is enumerating over) does not implement IMyInterface. Your code would work if you did something like this

IEnumerable<IMyInterface> implements = this.ListOfItems
                                           .OfType<IMyInterface>();

Assuming that list of items as something like this

private List<MyConcreteType> ListOfItems;
Darren Kopp
A: 

@Juan

No, OfType does an is statement on the object

so it be like

foreach (object item in items)
{
    if (item is IMyInterface)
        yield return item;
}

so if you are doing that on IEnumerable

foreach (Type item in items)
{
    if (item is IMyInterface)
        yield return item; // this will never fire
}

That's the issue. Go ahead and try it, and you will see that it doesn't work.

Proof of Concept

static void Main(string[] args)
    {
        var instance = new Class1();
        instance.GetType().Assembly.GetTypes().OfType<IMyInterface>().ToList().ForEach(p => Console.WriteLine("Match"));

        var instances = new List<object>
        {
            new Class1(),
            new Class1(),
            new DateTime(),
            new TimeSpan()
        };

        instances.OfType<IMyInterface>().ToList().ForEach(p => Console.WriteLine("Match found in list"));

        Console.Read();
    }

    public class Class1 : IMyInterface
    {

    }

    public interface IMyInterface
    {

    }
Darren Kopp
+1  A: 

About to test it...

EDIT: You are right Darren. However your solution (.Where(p => p.IsAssignableFrom(type)) also doesn't work, it only returns the interface and not the types implementing it

Juan Manuel
A: 

@Juan

haha, yeah, my logic was slightly backwards. Should be

var type = typeof(IMyInteraface);
var types = AppDomain.CurrentDomain
                     .GetAssemblies()
                     .SelectMany(s => s.GetTypes())    
                     .Where(type.IsAssignableFrom);

instead of

var type = typeof(IMyInteraface);
var types = AppDomain.CurrentDomain
                     .GetAssemblies()
                     .ToList()
                     .SelectMany(s => s.GetTypes())    
                     .Where(p => p.IsAssignableFrom(type));

I have updated the oringal accordingly

Darren Kopp
+1  A: 

Excellent, thank you. You won your "accepted answer" back ;)

(I only added a "(...) && p.IsInterface == false" (because I need to create instances of those types))

Thanks again

Juan Manuel
A: 

No problem Juan. And as always, have continued fun in the weird world of reflection :)

Darren Kopp