Evaldas Buinauskas
Evaldas Buinauskas

Reputation: 14097

Implementing generic interface with generic method

I've got interface as follows:

public interface IService<T> where T : class
{
    Task<IEnumerable<T>> GetAsync<U>(int subscriberId, U request) where U : SearchRequestBase;
}

I'm implementing the following way (CategoriesRequest inherits SearchRequestBase):

public async Task<IEnumerable<CategoriesResponse>> GetAsync<CategoriesRequest>(int subscriberId, CategoriesRequest request)
{
    // Implementation
}

But no matter what I do, I get compile errors, the only way to make it work was to put U generic in interface and put constraint there.

Is this recommended way to do it? Or can I declare generic method with constraint next to my method and implement it that way?

My intention is to put generic only as a return type and my input has to contain anything that inherits from SearchRequestBase.

Update

So this is my base class:

public class SearchRequestBase
{
    private const int minimumQueryLength = 3;
    private const int minimumResultsSize = 1;
    private const int maximumResultsSize = 100;
    private const int defaultResultsSize = 5;

    protected SearchRequestBase()
    {
    }

    [Required]
    [MinLength(minimumQueryLength, ErrorMessage = "Query string has to contain at least three characters")]
    public string Query { get; set; }

    [Range(minimumResultsSize, maximumResultsSize, ErrorMessage = "Size must be between 1 and 100")]
    public int Size { get; set; } = defaultResultsSize;
}

And CategoriesRequest class that now does not yet implement any extra properties (but will).

public class CategoriesRequest : SearchRequestBase
{
}

There are more requests that inherit from SearchRequestBase.

So now there's my IService interface:

public interface IService<T> where T : class
{
    Task<IEnumerable<T>> GetAsync(int subscriberId, SearchRequestBase request);
}

And I implement it in CategoryService:

public class CategoryService : IService<CategoriesResponse>
{
    private readonly IElasticClient _client;

    public CategoryService(IElasticClient client)
    {
        _client = client ?? throw new ArgumentNullException(nameof(client));
    }

    public async Task<IEnumerable<CategoriesResponse>> GetAsync(int subscriberId, CategoriesRequest request)
    {
        var descriptor = new CategoryBuilder().Build(subscriberId, request);

        var index = "categories";

        var response = await _client.SearchAsync<CategoriesResponse>(descriptor.Index(index));

        return response.Documents;
    }
}

However compiler curses at me and gives the following error:

Error   CS0535  'CategoryService' does not implement interface member 'IService<CategoriesResponse>.GetAsync(int, SearchRequestBase)'

Upvotes: 1

Views: 1206

Answers (1)

DavidG
DavidG

Reputation: 119186

Your method signature needs to be:

public async Task<IEnumerable<CategoriesResponse>> GetAsync(
    int subscriberId, SearchRequestBase request)

Otherwise you need to add another generic type parameter to the interface. For example:

public interface IService<TRequest, TResponse>
    where TRequest : SearchRequestBase
    where TResponse : class
{
    Task<IEnumerable<TResponse>> GetAsync(int subscriberId, TRequest request);
}

Upvotes: 2

Related Questions