Ngoc Pham
Ngoc Pham

Reputation: 443

Get all inherited classes of a generic abstract class

I'm looking for some way to get all classes that inherit a generic abstract class, and perform a method on each of those classes.

I've been following this Change parameter type when implementing an abstract method to have the class implementations I want, something similar to this:

public abstract class AbstractRequest<TResponseData>
                where TResponseData : IResponseData
{
    public abstract void Search();
    public abstract GoodData BindData(TResponseData data);
}

public interface IResponseData
{
}

public class AResponse : IResponseData
{
}

public class BResponse : IResponseData
{
}

public class A : AbstractRequest<AResponse>
{
    public override void Search()
    {
        // get AResponse
        // Call to BindData() with AResponse
    }
    public override GoodData BindData(AResponse data)
    {
        // Bind the data from AResponse to GoodData
    }
}

public class B : AbstractRequest<BResponse>
{
    public override void Search()
    {
        // Get BResponse
        // Call to BindData() with BResponse
    }
    public override GoodData BindData(BResponse data)
    {
        // Bind the data from BResponse to GoodData
    }
}    

This is working find, until I need to get all the A & B class and invoke the Search() method for each of the classes. With a non-generic abstract class, I could use the following snippet to get the classes

var instances = from t in Assembly.GetExecutingAssembly().GetTypes()
                where t.IsSubclassOf(typeof(AbstractRequest))
                    && t.GetConstructor(Type.EmptyTypes) != null
                select Activator.CreateInstance(t) as AbstractRequest;

Then, how can I get all the classes A and B that inherit from AbstractRequest<AResponse> and AbstractRequest<BResponse>?

Edit: I forgot to mention. Let's assume that there will be many implementations similar to A or B, and will be added over time. I would like to have an "elegant" (if possible) solution so later on, I only need to take care of the implementation C, D, etc.

Thanks!

Upvotes: 9

Views: 6577

Answers (3)

Shamster
Shamster

Reputation: 2212

This has worked for me on a few separate projects

/// <summary>
/// Find all subclasses of a given type via reflection
/// </summary>
/// <typeparam name="T">Parent class type</typeparam>
/// <returns></returns>
public Type[] GetAllSubclasses<T>() where T : class
{
    Type[] types = Assembly.GetExecutingAssembly().GetTypes()
                           .Where(t => t.IsSubclassOf(typeof (T))).ToArray();

    return types;
}

To instantiate and then execute a method, you can try this pattern:

// Create new instances, then use those to execute some method.
foreach (Type T in GetAllSubclasses<MyType>())
{
    MyType mt = (MyType) Activator.CreateInstance(T);
    mt.MyMethod(someArgs);
}

Upvotes: -1

Steve Czetty
Steve Czetty

Reputation: 6228

Actually getting the instance to invoke Search() (and only search) can be done by breaking up the abstract class:

public abstract class AbstractRequest
{
    public abstract void Search();
}

public abstract class AbstractRequest<TResponseData> : AbstractRequest
            where TResponseData : IResponseData
{
    public abstract GoodData BindData(TResponseData data);
}

And then you can use your original (non-generic) code:

var instances = from t in Assembly.GetExecutingAssembly().GetTypes()
    where t.IsClass &&
          typeof(AbstractRequest).IsAssignableFrom(t) &&
          t.GetConstructor(Type.EmptyTypes) != null
    select Activator.CreateInstance(t) as AbstractRequest;

(Old, busted solution)

I think that I would do something like this (untested):

var abstractRequestTypes =
    (from t in Assembly.GetExecutingAssembly().GetTypes()
     where t.IsClass &&
          typeof(IResponseData).IsAssignableFrom(t)
     select typeof(AbstractRequest<>).MakeGenericType(t)).ToList();

var instanceTypes = from t in Assembly.GetExecutingAssembly().GetTypes()
    where t.IsClass &&
          abstractRequestTypes.Any(dt => dt.IsAssignableFrom(t));

This should cover anything in the inheritance hierarchy, taking advantage of the type constraint on AbstractRequest.

Upvotes: 4

Selman Gen&#231;
Selman Gen&#231;

Reputation: 101681

Something like this should do the trick:

from t in Assembly.GetExecutingAssembly().GetTypes()
where t.IsClass && 
      (typeof(AbstractRequest<AResponse>).IsAssignableFrom(t) ||
       typeof(AbstractRequest<BResponse>).IsAssignableFrom(t))
select t;

Or if you wanna get all classes that inherit from generic class AbstractRequest you can use:

from t in Assembly.GetExecutingAssembly().GetTypes()
where t.IsClass && 
      t.BaseType != null &&
      t.BaseType.GetGenericTypeDefinition() == typeof(AbstractRequest<>)
select t;

Upvotes: 4

Related Questions