Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find all classes that implements a generic abstract class using reflection in C#?

I have a c# class that looks like this

public abstract class Listener<T> where T : Event
{
    public abstract void Handle(T _event);
}

I extend this class something like this

public class SendWelcomeEmail : Listener<UserWasCreated>
{
    public override void Handle(UserWasCreated _event)
    {
        //...
    }
}

I need to use reflection to find all classes that extend the Listener<> base class.

I tried the following

var listeners = AppDomain.CurrentDomain.GetAssemblies()
                         .SelectMany(assembly => assembly.GetTypes())
                         .Where(x => x.IsClass && !x.IsInterface)
                         .Where(listener => !listener.IsAbstract && listener.IsGenericType && listener.GetGenericTypeDefinition() == typeof(Listener<>))
                         .ToList();

But that does not return anything. This condition returns false all the time listener.GetGenericTypeDefinition() == typeof(Listener<>)

How can I correctly find all the classes that extend the Listener<> base class?

like image 321
Junior Avatar asked Nov 28 '22 21:11

Junior


2 Answers

Start by building up the infrastructure you need: put more tools in your toolbox, and then use those tools.

You want to list all the base types of a type, so list all the base types of a type:

static class Extensions
{
public static IEnumerable<Type> BaseTypes(this Type type)
{
    Type t = type;
    while (true)
    {
        t = t.BaseType;
        if (t == null) break;
        yield return t;
    }
}
}

Now we have a useful tool in our toolbox.

We have a type in hand. We wish to know if something is true of any of its base types. Therefore we should be using Any:

static bool AnyBaseType(this Type type, Func<Type, bool> predicate) =>
  type.BaseTypes().Any(predicate);

Now we have another useful tool.

We want to know if a particular type is a particular generic:

static bool IsParticularGeneric(this Type type, Type generic) =>
  type.IsGenericType && type.GetGenericTypeDefinition() == generic;

We want to know if a particular type is a listener:

static bool IsListener(Type type) =>
  type.IsParticularGeneric(typeof(Listener<>));

Now we have the tools we need.

var listeners = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                from type in assembly.GetTypes()
                where type.AnyBaseType(IsListener)
                select type;

See how much easier the query is to read when you build up the tools you need one at a time? What do we want to know? If any base type is a listener. So how does the code read? "where type any base type is listener" -- the code reads like a description of what it does.

like image 150
Eric Lippert Avatar answered May 20 '23 00:05

Eric Lippert


You can find out is any base type is a Listener<>, by recursively checking is target type IsInheritedFrom it:

public static class Extension
{
    public static bool IsInheritedFrom(this Type type, Type Lookup)
    {
        var baseType = type.BaseType;
        if (baseType == null)
            return false;

        if (baseType.IsGenericType
                && baseType.GetGenericTypeDefinition() == Lookup)
            return true;

        return baseType.IsInheritedFrom(Lookup);
    }
}

var lookup = typeof(Listener<>);
var listeners = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(assembly => assembly.GetTypes())
    .Where(x => x.IsClass && !x.IsAbstract && x.IsInheritedFrom(lookup))
    .ToList();
like image 23
Slava Utesinov Avatar answered May 19 '23 23:05

Slava Utesinov