如何使用C#中的反射查找实现通用抽象类的所有类?

时间:2018-10-19 05:10:05

标签: c# linq reflection

我有一个c#类,看起来像这样

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

我将此类扩展为此类

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

我需要使用反射来查找扩展Listener<>基类的所有类。

我尝试了以下

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();

但这不会返回任何内容。此条件始终listener.GetGenericTypeDefinition() == typeof(Listener<>)

返回false

如何正确找到扩展Listener<>基类的所有类?

2 个答案:

答案 0 :(得分:11)

首先建立所需的基础结构:在工具箱中放置更多工具,然后使用这些工具。

您要列出某个类型的所有基本类型,因此请列出该类型的所有基本类型:

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;
    }
}
}

现在我们的工具箱中有一个有用的工具。

我们有一种类型。我们希望知道某些基本类型是否正确。因此,我们应该使用Any

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

现在我们有了另一个有用的工具。

我们想知道特定类型是否为特定泛型:

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

我们想知道特定类型是否为侦听器:

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

现在我们有了所需的工具。

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

看看您一次建立一个所需的工具时,查询的可读性如何?我们想知道什么? 如果任何基本类型是侦听器。那么代码如何读取? “任何基本类型都是侦听器的类型”-代码的读取方式类似于其功能的描述。

答案 1 :(得分:2)

您可以通过递归检查目标类型为Listener<>来确定它是IsInheritedFrom的任何基本类型:

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();