2

How can I find all classes in an assembly that are an instance of a generic abstract class and implement a certain interface ?

Note:
The interface can also be implemented in a class that is inherited from another class that implements the interface.

A concrete example:
I have the bellow interface and Middleware-Class:

public interface IHttpHandler
{
    bool IsReusable { get; }
    void ProcessRequest(HttpContext context);
}

public abstract class HandlerMiddleware<T> where T: IHttpHandler
{

    private readonly RequestDelegate _next;

    public HandlerMiddleware()
    { }

    public HandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }


    public async Task Invoke(HttpContext context)
    {    
        await SyncInvoke(context);
    }


    public Task SyncInvoke(HttpContext context)
    {
        // IHttpHandler handler = (IHttpHandler)this;
        T handler = System.Activator.CreateInstance<T>();

        handler.ProcessRequest(context);
        return Task.CompletedTask;
    }


} // End Abstract Class HandlerMiddleware

How can I find all classes like HelloWorldHandler, that implement the abstract class, and implement IHttpHandler.

Note that HandlerMiddleware is generic.
It should find all handlers, e.g. HelloWorldHandler1 and HelloWorldHandler2.

[HandlerPath("/hello")]
public class HelloWorldHandler 
    : HandlerMiddleware<HelloWorldHandler>, IHttpHandler
{
    public HelloWorldHandler() :base() { }
    public HelloWorldHandler(RequestDelegate next):base(next) { }

    void IHttpHandler.ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        //await context.Response.WriteAsync("Hello World!");
        byte[] buffer = System.Text.Encoding.UTF8.GetBytes("hello world");
        context.Response.Body.Write(buffer, 0, buffer.Length);
    }

    bool IHttpHandler.IsReusable
    {
        get
        {
            return false;
        }
    }

}

Bonus points if the method also find this construct:

[HandlerPath("/hello2")]
public class HelloWorldHandler2
: HandlerMiddleware<Middleman>
{

    public HelloWorldHandler2() :base() { }
    public HelloWorldHandler2(RequestDelegate next):base(next) { }

}


public class Middleman
    : IHttpHandler
{
    bool IHttpHandler.IsReusable => throw new NotImplementedException();

    void IHttpHandler.ProcessRequest(HttpContext context)
    {
        throw new NotImplementedException();
    }
}
  • All Classes of structure HandlerMiddleware where has implemented IHttpHandler (doesn't matter if the implementation itselfs throws not implemented or not). –  Nov 03 '17 at 13:09
  • What's the context of this "find"? Are you looking for advice on how to use Visual Studio or are you wanting to write reflection code that performs this search at runtime? – Damien_The_Unbeliever Nov 03 '17 at 13:09
  • @Damien_The_Unbeliever: Good question. Search at runtime I mean. –  Nov 03 '17 at 13:10
  • Just use `typeof(HandlerMiddleware).IsAssignableFrom(type) && typeof(IHttpHandler).IsAssignableFrom(type)`. Expand into `typeof(type-in-assembly).Assembly.GetTypes().Where(t => typeof(HandlerMiddleware).IsAssignableFrom(t)&& typeof(IHttpHandler).IsAssignableFrom(type)).ToArray();` – Paul Suart Nov 03 '17 at 13:50
  • Possible duplicate of [Getting all types that implement an interface](https://stackoverflow.com/questions/26733/getting-all-types-that-implement-an-interface) – Paul Suart Nov 03 '17 at 13:57

2 Answers2

2

Your problem is actually quite complex to grasp.
The problem is that your class is derived from a generic class, whose generic argument implements the IHttpHandler.

You need to get the inherited (base) type (because you inherit from it).
That's a generic type, so you need to check if it's a generic type.
If it es, you need to get the generic type (GetGenericTypeDefinition).
Then you need to check if the generic type is of type HandlerMiddleware<>
Then you need to get the argument from the generic type.
Then you need to check the first generic argument (if it has one).
Then you need to check if that generic argument's type (or it's derivation base) implements the interface in question.

So in your case:

var ls = FindDerivedTypes(t.Assembly, typeof(HandlerMiddleware<>), typeof(IHttpHandler));
        System.Console.WriteLine(ls);



public static List<System.Type> FindDerivedTypes(Assembly assembly
    , System.Type typeToSearch
    ,System.Type neededInterface)
{
    List<System.Type> ls = new List<System.Type>();

    System.Type[] ta = assembly.GetTypes();

    int l = ta.Length;
    for (int i = 0; i < l; ++i)
    {
        if (ta[i].BaseType == null)
            continue;

        if (!ta[i].BaseType.IsGenericType)
            continue;

        // public class Middleman : IHttpHandler
        // public class HelloWorldHandler2 : HandlerMiddleware<Middleman>
        // public class HelloWorldHandler : HandlerMiddleware<HelloWorldHandler>, IHttpHandler

        var gt = ta[i].BaseType.GetGenericTypeDefinition();
        if (gt == null)
            continue;

        if (!object.ReferenceEquals(gt, typeToSearch))
            continue;

        Type[] typeParameters = ta[i].BaseType.GetGenericArguments();
        if (typeParameters == null || typeParameters.Length < 1)
            continue;

        if(neededInterface.IsAssignableFrom(typeParameters[0]))
            ls.Add(ta[i]);
    } // Next i 

    return ls;
} // End Function FindDerivedTypes

And that's how you get that list.

Stefan Steiger
  • 78,642
  • 66
  • 377
  • 442
-1

This gives you all classes that implement IHttpHandler in the assemblies of the current domain:

 List<Type> result = new List<Type>();

        var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();

        foreach(var assem in assemblies)
        {
            var list = assem.GetExportedTypes().Where(t => t.GetInterfaces().Contains(typeof(IHttpHandler))).ToList();

            if (list != null && list.Count != 0)
            {
                result.AddRange(list);
            }
        }

To check if it derives from a generic abstract class you can use following:

static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
    {
        while (toCheck != null && toCheck != typeof(object))
        {
            var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
            if (generic == cur)
            {
                return true;
            }
            toCheck = toCheck.BaseType;
        }
        return false;
    }

see: Check if a class is derived from a generic class

Usage: IsSubclassOfRawGeneric(typeof(HandlerMiddleware<>), typeToCheck)

Reno
  • 542
  • 3
  • 13