Reputation: 443
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
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
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
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