使用反射在程序集类型中查找接口<T>

Use Reflection to Find Interface<T> in Assembly Types

我不确定是不是尖括号导致了我的问题,但我无法在 Stack Overflow 答案中找到答案。如果已经有了,欢迎您为我指明正确的方向。

总而言之,我有一个接口,但无法通过反射找到它。让我们开始吧:

接口

interface ICrew
{
    string Name { get; set; }
}

interface IDataService<T>
{
    IEnumerable<T> GetItems();
}

class Crew : ICrew
{
    public string Name { get; set; }
}

class CrewRepository : IDataService<ICrew>
{
    DbContext _context { get; }

    public CrewRepository(DbContext context)
    {
        _context = context;
    }


    public IEnumerable<ICrew> GetItems()
    {
        return _context.Crews;
    }
}

class DbContext
{
    public DbContext()
    {
        Crews = new List<Crew>();
    }
        
    public List<Crew> Crews;
}

代码

class Program
{
    private static DbContext _context;

    static void Main(string[] args)
    {
        _context = new DbContext();

        //I want to find CrewRepository (which is type of IDataService<ICrew>)
        var dataService = Get(typeof(ICrew));
    }

    private static object Get(Type T)
    {
        var types = typeof(CrewRepository).Assembly.GetTypes();

        var test = types.Where(t => t.IsAssignableFrom(T)); //Finds ICrew

        var test2 = types.Where(t => t.GetInterfaces().Contains(T)); //Finds Crew

        var test3 = types.Where(t => t.GetInterfaces().Contains(typeof(IDataService<>))); //Finds nothing

        var test4 = types.Where(t => t.IsAssignableFrom(typeof(IDataService<>))); //Finds interface IDataService

        var test5 = types.Where(t => typeof(IDataService<>).IsAssignableFrom(t)); //Finds interface IDataService

        var instance = types
            .Where(t => t.IsAssignableFrom(T))
            .FirstOrDefault();

        if (instance != null)
        {
            return Activator.CreateInstance(instance, _context);
        }

        return default;
    }

}

我不知道如何正确查询程序集类型以获得 IDataService<T>。我的 Get 方法直到运行时才知道要请求什么类型,并且只有在编译时知道类型时才能使用尖括号内的泛型。任何人都可以帮助我弄清楚我应该在这里做什么才能通过反射获得 Get 到 return 正确的 class 存储库?

如评论中所述,必须使用 Type.MakeGenericType 方法在运行时创建所需的类型 IDataService<"T">。您的 Get 方法可能如下所示:

private static object Get(Type t)
{
    var types = typeof(CrewRepository).Assembly.GetTypes();
    var runtimeType = typeof(IDataService<>).MakeGenericType(t);
    var type = types.SingleOrDefault(x => x.GetInterfaces().Contains(runtimeType));

    if (type != null)
    {
        return Activator.CreateInstance(type, _context);
    }

    return null;
}

演示:https://dotnetfiddle.net/vRJzr8

好的,这已经得到回答,但作为对那个答案的补充(我完全同意并且自己构建了这个项目,我意识到我的代码几乎完全匹配@thehennyy's),有这个:

就个人而言,与其询问特定接口是否“是根据 Type.GetInterfaces() 的接口之一”,我更愿意询问实现给定接口的类型.基本相同,但语义可能很重要。

所以我的代码是这样的:

private static object Get2(Type t)
{
    // Create the closed generic type that we need
    var closedType = typeof(IDataService<>).MakeGenericType(t);

    var allTypes = typeof(CrewRepository).Assembly.GetTypes();

    var dataServiceType = allTypes.Single(x => closedType.IsAssignableFrom(x));

    return Activator.CreateInstance(dataServiceType, _context);
}

但是,如果我们使用泛型,我们可以做得更好并完全避免 closed/open 泛型业务:

private static IDataService<T> Get3<T>()
{
    var allTypes = typeof(CrewRepository).Assembly.GetTypes();

    var dataServiceType = allTypes.Single(x => typeof(IDataService<T>).IsAssignableFrom(x));

    return (IDataService<T>)Activator.CreateInstance(dataServiceType, _context);
}

static void Main(string[] args)
{
    _context = new DbContext();

    //I want to find CrewRepository (which is type of IDataService<ICrew>)
    var dataService3 = Get3<ICrew>();
}