Search code examples
c#.net-coresystem.reflection

Get all sub classes of generic abstract base class


I have a generic base class and I eventually would have many derived classes. I am trying to write a function which would return all these sub classes but everything I have tried so far has not worked.

public abstract class Index<T>
    where T : class
{
    public abstract string IndexName { get; }

    public abstract string TypeName { get; }

    public abstract Expression<Func<T, IConvertible>> IdFieldSelector { get; }

    public string MakeSearchId(T item)
    {
        return ToSearchId(IdFieldSelector.Compile().Invoke(item));
    }

    public string MakeSearchId(IConvertible idValue)
    {
        return ToSearchId(idValue);
    }

    private static string ToSearchId(IConvertible idValue)
    {
        return idValue.ToString(CultureInfo.InvariantCulture);
    }
}

sample subclass:

public class SurveyChangedIndex : Index<SurveyChanged>
{
    public override string IndexName => "reviews";

    public override string TypeName => "review";

    public override Expression<Func<SurveyChanged, IConvertible>> IdFieldSelector => sc => sc.SurveyResponseId;
}

sample function:

        var indexBase = typeof(Index<>);
        var indexes = Assembly.GetAssembly(indexBase)
            .GetTypes()
            .Where(type =>
                type != indexBase &&
                !type.IsInterface &&
                !type.IsAbstract &&
                type.BaseType == indexBase)
            .ToList();

Solution

  • You can do the following (C# 7 syntax follows):

    public static IEnumerable<Type> GetAllDescendantsOf(
        this Assembly assembly, 
        Type genericTypeDefinition)
    {
        IEnumerable<Type> GetAllAscendants(Type t)
        {
            var current = t;
    
            while (current.BaseType != typeof(object))
            {
                yield return current.BaseType;
                current = current.BaseType;
            }
        }
    
        if (assembly == null)
            throw new ArgumentNullException(nameof(assembly));
    
        if (genericTypeDefinition == null)
            throw new ArgumentNullException(nameof(genericTypeDefinition));
    
        if (!genericTypeDefinition.IsGenericTypeDefinition)
            throw new ArgumentException(
                "Specified type is not a valid generic type definition.", 
                nameof(genericTypeDefinition));
    
        return assembly.GetTypes()
                       .Where(t => GetAllAscendants(t).Any(d =>
                           d.IsGenericType &&
                           d.GetGenericTypeDefinition()
                            .Equals(genericTypeDefinition)));
    }
    

    This will return any type that inherits directly or indirectly from the specified generic type definition.

    In the following scenario:

    class Base { }
    class Base<T>: Base { }
    class Foo : Base<int> { }
    class Bar : Base<string> { }
    class Frob : Bar { }
    class FooBar: Base { };
    
    var genericTypeDefinition = typeof(Base<>);
    var types = Assembly.GetExecutingAssembly()
                        .GetAllDescendantsOf(genericTypeDefinition)));
    

    GetAllDescendantsOf will output Foo, Bar and Frob.