首页 > 解决方案 > 获取 .net core 中的开放通用类型列表

问题描述

尝试根据名称动态解析特定的实现(除非有更好的方法)

有客户:

public interface IClient { }

public interface IClient<TModel> : IClient where TModel : ListingBase // ListingBase is abstract class
{
    public Task<TModel> MapToObject(Stream file);
}

// base class with common funcationality
public abstract class ClientBase<TModel> : IClient<TModel> where TModel : ListingBase
{
    protected ClientBase(IHttpClient httpClient)
    {
        HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
    }

    public IHttpClient HttpClient { get; }
    
    public abstract Task<TModel> MapToObject(Stream file);
    ... // other stuff
}

实现:

public class Client1 : ClientBase<Model1> // Model1 inherits from ListingBase
    {
{

    public override Task<Model1> MapToObject(Stream file) // Model1 inherits from ListingBase
    {
        // do mapping from Json / Stream to object


        return Task.FromResult(new Model1());
    }
}

// Another client

public class Client2 : ClientBase<Model2> // Model2 inherits from ListingBase
    {
{

    public override Task<Model2> MapToObject(Stream file) // Model2 inherits from ListingBase
    {
        // do mapping from Json / Stream to object


        return Task.FromResult(new Model2());
    }
}

然后在某个服务的某个地方注入 ClientFactory,我需要根据配置中指定的“名称”获取正确的客户端:

public interface IClientFactory
{
    IClient<ListingBase> GetClient(string clientName);
}

public class ClientFactory : IClientFactory
{
    private readonly IServiceProvider _serviceProvider;

    public ClientFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
    }

    public IClient<ListingBase> GetClient(string clientName)
    {
        
        var clients = _serviceProvider.GetServices(typeof(IClient)); // this works - I get a list of 2 
        // match by name

        var client = clients.FirstOrDefault(); // TODO - but for now get first
        
        return (IClient<ListingBase>) client; // failing here - invalid cast 
    }

}

错误:

无法将“Client1”类型的对象转换为“Abstract.IClient”1..

在某些服务中,希望根据字符串获取特定客户端:

public class DefaultProcessor 
{
    private readonly IListingRepository _repository;

    public DefaultProcessor(
        IClientFactory clientFactory, 
        ...
        Client = clientFactory.GetClient("name-from-config");
    }
    

    public async Task Write(Stream file, CancellationToken cancellationToken = default)
    {
        var model = await Client.MapToObject(file);
        ...
    }
}

标签: c#.netasp.net-core

解决方案


要将SaborClient(我想实现IClient<?>)转换为IClient<ListingBase>您需要IClient<T>是协变的T,但为此您也需要Task<T>是协变的T。事实并非如此,因为 Task<>类和方差声明仅在接口上可用。

您可以使用构造良好的界面绕过此限制,请参阅此答案

这里是一个实现示例(您需要导入MorseCode.ITask NuGet 包):

public abstract class ListingBase
{
}

public class Model1 : ListingBase
{
}

public interface IClient
{
}

// Note the *out* here
public interface IClient<out TModel> : IClient where TModel : ListingBase
{
    public ITask<TModel> MapToObject(Stream file);
}

public class Client1 : IClient<Model1>
{
    public async ITask<Model1> MapToObject(Stream file)
    {
        return new Model1();
    }
}

public class ClientFactory
{
    public IClient<ListingBase> GetClient()
    {
        var client = GetClientInternal();
        return (IClient<ListingBase>) client;
    }

    private static IClient GetClientInternal() => new Client1();
}

class Program
{
    static void Main(string[] args)
    {
        var factory = new ClientFactory();
        var client = factory.GetClient();
        Console.WriteLine("Client:" + client);
        Console.ReadLine();
    }
}

推荐阅读