38

Using reflection, is it possible to discover all types that derive from a given type?

Presumably the scope would be limited to within a single assembly.

xyz
  • 27,223
  • 29
  • 105
  • 125

5 Answers5

48

pretty much the same as Darin's but here you go..

    public static List<Type> FindAllDerivedTypes<T>()
    {
        return FindAllDerivedTypes<T>(Assembly.GetAssembly(typeof(T)));
    }

    public static List<Type> FindAllDerivedTypes<T>(Assembly assembly)
    {
        var baseType = typeof(T);
        return assembly
            .GetTypes()
            .Where(t =>
                t != baseType &&
                baseType.IsAssignableFrom(t)
                ).ToList();

    } 

used like:

var output = FindAllDerivedTypes<System.IO.Stream>();
            foreach (var type in output)
            {
                Console.WriteLine(type.Name);
            }

outputs:

NullStream
SyncStream
__ConsoleStream
BufferedStream
FileStream
MemoryStream
UnmanagedMemoryStream
PinnedBufferMemoryStream
UnmanagedMemoryStreamWrapper
IsolatedStorageFileStream
CryptoStream
TailStream
Matt Smith
  • 17,026
  • 7
  • 53
  • 103
Hath
  • 12,606
  • 7
  • 36
  • 38
  • 1
    I assume you're using `IsAssignableFrom` instead of `IsSubclassOf` to account for interfaces. But calling it on an interface also returns its implementing classes. – HappyNomad Oct 25 '16 at 07:06
32
var derivedTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
                   where t.IsSubclassOf(typeof(A))
                   select t;
JG in SD
  • 5,427
  • 3
  • 34
  • 46
Sergej Andrejev
  • 9,091
  • 11
  • 71
  • 108
  • This is worth mentioning this query only retrieves interfaces or classes that directly derive from the type `A`. – Jämes Sep 22 '16 at 09:31
  • 2
    @ZenLulz The [`IsSubclassOf` method](https://msdn.microsoft.com/en-us/library/system.type.issubclassof.aspx) name is misleading. Actually, it returns `true` for *derived classes*. – HappyNomad Oct 25 '16 at 06:53
13
var types = Assembly
    .GetExecutingAssembly()
    .GetTypes()
    .Where(t => typeof(SomeBaseType).IsAssignableFrom(t) && 
                t != typeof(SomeBaseType))
    .ToArray();
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
6

I rewrote @Hath's excellent answer as an extension method:

public static class TypeExtensions
{
    public static List<Type> GetAllDerivedTypes(this Type type)
    {
        return Assembly.GetAssembly(type).GetAllDerivedTypes(type);
    }

    public static List<Type> GetAllDerivedTypes(this Assembly assembly, Type type)
    {
        return assembly
            .GetTypes()
            .Where(t => t != type && type.IsAssignableFrom(t))
            .ToList();
    }
}

Usage:

var listOfDerived = typeof(MyClass).GetAllDerivedTypes();
Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
  • 3
    Why use [`IsAssignableFrom`](https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom.aspx) instead of [`IsSubclassOf`](https://msdn.microsoft.com/en-us/library/system.type.issubclassof.aspx)? Maybe you want to support interfaces? But that result will include implementing classes. – HappyNomad Oct 25 '16 at 07:16
  • @HappyNomad You make an excellent point. In my case, I wanted to support interfaces. – Nate Barbettini Oct 25 '16 at 16:46
  • My [answer](http://stackoverflow.com/a/40238277) for a duplicate question supports interfaces. – HappyNomad Oct 26 '16 at 04:58
0

I know the OP specified within a single assembly, but since this question is one of the top results when trying to find how to do this I am giving an example of how to do this with all assemblies that derive from the assembly containing the given type.

/// <summary>
/// Gets all types derived from the given <paramref name="type"/>
/// <para>By default checks the assembly of the given type and all dependent assemblies.</para>
/// </summary>
/// <param name="type"></param>
/// <param name="allAssemblies">If false only the assembly containing the type will be checked.</param>
/// <returns></returns>
public static IEnumerable<Type> GetAllDerivedTypes(this Type type, bool allAssemblies = true)
{
    List<Type> derivedTypes = type.GetAllDerivedTypes(Assembly.GetAssembly(type)!).AsList();

    if (allAssemblies)
    {
        IEnumerable<Assembly> dependentAssemblies = GetDependentAssemblies(Assembly.GetAssembly(type)!);
        derivedTypes.AddRange(dependentAssemblies.SelectMany(a => GetAllDerivedTypes(type, a)));
    }

    return derivedTypes;
}

private static IEnumerable<Assembly> GetDependentAssemblies(Assembly analyzedAssembly)
{
    return AppDomain.CurrentDomain.GetAssemblies()
        .Where(a => a.GetReferencedAssemblies()
            .Select(assemblyName => assemblyName.FullName)
            .Contains(analyzedAssembly.FullName));
}

private static IEnumerable<Type> GetAllDerivedTypes(this Type baseType, Assembly assembly)
{
    TypeInfo baseTypeInfo = baseType.GetTypeInfo();

    return assembly.DefinedTypes.Where(type =>
    {
        if (baseTypeInfo.IsClass)
        {
            return type.IsSubclassOf(baseType);
        }

        return baseTypeInfo.IsInterface && type.ImplementedInterfaces.Contains(baseTypeInfo.AsType());

    }).Select(type => type.AsType());
}
Keith Banner
  • 602
  • 1
  • 10
  • 15