12

related to Getting all types that implement an interface we can easily get all Types in the Assembly that implements a specific interface.

Example:

interface IFace
{
}

class Face : IFace
{
}

class TwoFace : Face
{
}

For this structure, we will find both classes via reflection, i.e. all classes that are derived from the first implementation too, using

GetTypes().Where(
  type => type.GetInterfaces().Contains(typeof(IFace))
)

So the question is: how can I restrict the result to the base class that initially implements the interface?! In this example: only class type Face is relevant.

Community
  • 1
  • 1
childno͡.de
  • 4,679
  • 4
  • 31
  • 57
  • Note: iterating all found classes/types, checking for if one is derived from another not seems practical, neither performant to me – childno͡.de May 19 '14 at 10:15
  • That's a limitation of using reflection. An interface doesn't know which classes implement it so you can't get at them without iterating over everything. If you want better performance, you probably need Roslyn. – Panagiotis Kanavos May 19 '14 at 10:18
  • 3
    @childno.de once you are already iterating all types in the assembly, it isn't going to have any additional performance impact to simply check the inheritance of thm – Marc Gravell May 19 '14 at 10:18
  • You could trick it by having unwanted classes to implement some `IFaceFake` interface in addition. – Sinatr May 19 '14 at 10:18
  • 1
    @childno.de: Do you have *evidence* for thinking it's impractical? It's almost never a good idea to guess about performance. – Jon Skeet May 19 '14 at 10:40
  • @MarcGravell / JonSkeet ok, I have to test if it really matters, but it's code that runs in realtime application context, so yes, for me performance matters. (nevertheless, reflecting is a really bad idea here under this circumstances ;D but I can't avoid it when loading assemblies by runtime. So I intend to avoid any useless actions / iterations where possible) – childno͡.de May 19 '14 at 10:47
  • @childno.de you misunderstand me; you are *already* using reflection here - it is simply the "check for subtypes" that isn't going to matter. If you don't want to use reflection, then either hard-code the list, or put the list into a configuration file somewhere (still *some* reflection to load by name, but much much less). – Marc Gravell May 19 '14 at 11:14
  • @MarcGravell no, didn't misunderstood you ;) I just wanted to point out that it is clear to me using reflection has more impact to performance than an additional iterator (I can't meter yet). – childno͡.de May 19 '14 at 11:26

1 Answers1

15

Firstly, I'd use Type.IsAssignableFrom rather than GetInterfaces, but then all you need to do is exclude types whose parent type is already in the set:

var allClasses = types.Where(type => typeof(IFace).IsAssignableFrom(type))
                      .ToList(); // Or use a HashSet, for better Contains perf.
var firstImplementations = allClasses
      .Except(allClasses.Where(t => allClasses.Contains(t.BaseType)));

Or as noted in comments, equivalently:

var firstImplementations = allClasses.Where(t => !allClasses.Contains(t.BaseType));

Note that this will not return a class which derives from a class which implements an interface, but reimplements it.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Why not just `allClasses.Where(t => !allClasses.Contains(t.BaseType))`? – Ben Aaronson May 19 '14 at 10:22
  • @BenAaronson: `Except` is generally more efficient, that's all. But yes, they're basically equivalent. – Jon Skeet May 19 '14 at 10:22
  • It is? That feels like it wants a question of its own! – Ben Aaronson May 19 '14 at 10:26
  • 1
    @BenAaronson: Not really - just read https://msmvps.com/blogs/jon_skeet/archive/2010/12/30/reimplementing-linq-to-objects-part-17-except.aspx (In this case, it may not be more efficient, in fact. I think I haven't had enough coffee.) – Jon Skeet May 19 '14 at 10:27
  • ok, thank you so far. seems to that it rans into an StackOverflow for me when using restricted `allClasses` again in the inner linq expression.. using outer `types` for `Contains(baseType)` check works fine, even it iterates the whole assembly type list again ;( – childno͡.de May 19 '14 at 11:38
  • @childno.de: What do you mean by "restricted"? And when you say "seems to that it rans into an StackOverflow" do you mean you'd expect it to, or that it actually does? I can't see why there's be a stack overflow problem at all. – Jon Skeet May 19 '14 at 11:39
  • @JonSkeet with "restricted" I meant the already filtered list from the first Where. I actually ran into one `System.Linq.Enumerable+c__Iterator1D`1[System.Type].MoveNext ()`. But it's not plain .net stack but a derived Mono .. so perhaps there is a bug in that implementation. – childno͡.de May 19 '14 at 11:51
  • @childno.de: It's hard to understand exactly what you mean without some test code. With the exact code I've given, there should be no problem. If you could provide a short but complete program that demonstrates the problem, I should be able to help more. – Jon Skeet May 19 '14 at 11:55
  • I found the missing paren, here it is: ) – ogrim Oct 15 '15 at 17:52