获取泛型类的子类型列表

本文关键字:类型 列表 泛型类 获取 | 更新日期: 2023-09-27 18:12:56

我有一个具有许多子类型的泛型类:

public abstract class MyClass<T> : MyBaseClass where T : class
{...}
public class MySubClassA : MyClass<A>
{...}
public class MySubClassB : MyClass<B>
{...}

是否有一种简单的方法来搜索MyClass的子类并获得包含MySubClassA和MySubClassB的IEnumerable<Type> ?

我以前使用过这个方法,但我不确定如何适应它用于泛型:

public static IEnumerable<Type> GetSubTypesOf(Type t, bool baseAssemblyOnly = false)
{
    List<Type> types = new List<Type>();
    Assembly[] searchAssemblies = baseAssemblyOnly
        ? new[] { Assembly.GetAssembly(t) }
        : AppDomain.CurrentDomain.GetAssemblies();
    foreach (Assembly a in searchAssemblies)
    {
        types.AddRange(a.GetTypes()
                        .Where(myType => myType.IsClass
                                      && !myType.IsAbstract
                                      && myType.IsSubclassOf(t)));
    }
    return types;
}

获取泛型类的子类型列表

这有点复杂,因为您必须搜索类型的基本类型才能找到与MyClass<>的开放泛型类型定义匹配的类型。您可以定义两个帮助器方法:

public static IEnumerable<Type> BaseTypesOf(Type t)
{
    while (t != null)
    {
        yield return t;
        t = t.BaseType;
    }
}
public static Type FindGenericBaseTypeOf(Type t, Type openType)
{
    return BaseTypesOf(t)
        .FirstOrDefault(bt => bt.IsGenericType && bt.GetGenericTypeDefinition() == openType);
}

则可以将它们应用到输入的类型序列中进行搜索,例如

var types = Assembly.GetExecutingAssembly().GetTypes()
    .Where(t => t.IsClass && !t.IsAbstract)
    .Select(t => new { Type = t, GenericBase = FindGenericBaseTypeOf(t, typeof(MyClass<>)) })
    .Where(ts => ts.GenericBase != null)
    .Select(ts => ts.GenericBase.GetGenericArguments().First())
    .ToArray();

问题是,当您为t参数传递typeof(MyClass<>)时,您不是传递实例化的泛型类型,而是传递泛型类型定义。这意味着您的所有类都不会响应IsSubclassOf(t)

你可以这样修改代码:

List<Type> types = searchAssemblies
    .SelectMany(a => 
        a.GetTypes()
        .Where(myType => myType.IsClass && !myType.IsAbstract && HasGenericBase(myType, t))
    ).ToList();
...
private static bool HasGenericBase(Type myType, Type t) {
    Debug.Assert(t.IsGenericTypeDefinition);
    while (myType != typeof(object)) {
        if (myType.IsGenericType && myType.GetGenericTypeDefinition() == t) {
            return true;
        }
        myType = myType.BaseType;
    }
    return false;
}