У меня проблема с выводом типа и компилятором C #. Прочитав этот вопрос и этот вопрос Я думаю, я понимаю, почему это не работает: я хотел бы знать, есть ли способ обойти проблему в чтобы получить предпочитаемую семантику вызова.
Вот некоторый код, который иллюстрирует мою проблему (извините за длину, это самый короткий, который я мог бы уменьшить):
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace StackOverflow
{
interface IQuery<TResult> { }
class MeaningOfLifeQuery : IQuery<int> { }
interface IQueryHandler<TQuery, TResult> where TQuery : class, IQuery<TResult>
{
Task<TResult> ExecuteAsync(TQuery query);
}
class MeaningOfLifeQueryHandler : IQueryHandler<MeaningOfLifeQuery, int>
{
public Task<int> ExecuteAsync(MeaningOfLifeQuery query)
{
return Task.FromResult(42);
}
}
interface IRepository
{
Task<TResult> ExecuteQueryDynamicallyAsync<TResult>(IQuery<TResult> query);
Task<TResult> ExecuteQueryStaticallyAsync<TQuery, TResult>(TQuery query)
where TQuery : class, IQuery<TResult>;
}
class Repository : IRepository
{
public Repository(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
readonly IServiceProvider _serviceProvider;
public async Task<TResult> ExecuteQueryDynamicallyAsync<TResult>(IQuery<TResult> query)
{
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
dynamic handler = _serviceProvider.GetRequiredService(handlerType);
return await handler.ExecuteAsync((dynamic) query);
}
public async Task<TResult> ExecuteQueryStaticallyAsync<TQuery, TResult>(TQuery query)
where TQuery : class, IQuery<TResult>
{
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(typeof(TQuery), typeof(TResult));
var handler = (IQueryHandler<TQuery, TResult>) _serviceProvider.GetRequiredService(handlerType);
return await handler.ExecuteAsync(query);
}
}
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection();
services.AddTransient<IQueryHandler<MeaningOfLifeQuery, int>, MeaningOfLifeQueryHandler>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
var repository = new Repository(serviceProvider);
var query = new MeaningOfLifeQuery();
int result = repository.ExecuteQueryDynamicallyAsync(query).Result;
result = repository.ExecuteQueryStaticallyAsync<MeaningOfLifeQuery, int>(query).Result;
// result = repository.ExecuteQueryStaticallyAsync(query).Result;
// Doesn't work: type arguments cannot be inferred
}
}
}
В основном я хочу, чтобы репозиторий выполнял запросы, просто вызывая метод ExecuteAsync
и передавая запрос. Чтобы заставить это работать, я должен использовать dynamic
для разрешения типа обработчика запросов во время выполнения согласно ExecuteQueryDynamicallyAsync
. В качестве альтернативы я могу указать как тип запроса, так и тип результата в качестве параметров типа при вызове метода (согласно ExecuteQueryStaticallyAsync
), но это, очевидно, довольно многословный вызов, особенно когда типы запросов и / или возвращаемых значений имеют длинные имена.
Я могу заставить компилятор выводить оба типа, используя сигнатуру метода, подобную этой:
Task<TResult> ExecuteQueryAsync<TQuery, TResult>(TQuery query, TResult ignored)
where TQuery : class, IQuery<TResult>
, что позволяет мне сделать что-то вроде этого:
var query = new MeaningOfLifeQuery();
...
... ExecuteQueryAsync(query, default(int)) ...
но затем я должен передавать фиктивное значение (которое игнорируется) методу при каждом вызове. Изменение подписи на это:
Task<TResult> ExecuteQueryAsync<TQuery, TResult>(TQuery query, TResult ignored = default(TResult))
where TQuery : class, IQuery<TResult>
, чтобы избежать передачи значения, когда вызов не работает, поскольку компилятор больше не может выводить тип результата (т. Е. Обратно в квадрат).
Я немного растерялся относительно того, как я могу решить эту проблему, или даже если она разрешима. Похоже, мои единственные варианты - придерживаться простой семантики вызовов (но полагаться на dynamic
) или каким-либо образом изменить мою архитектуру. Есть ли способ получить семантику вызова, которую я хочу?