我在类型推断和C#编译器方面遇到问题。阅读this question和this question后,我想我知道为什么它不起作用:我想知道的是,是否有任何办法可以解决该问题,以便获得我喜欢的电话语义。
下面的代码说明了我的问题(很抱歉,长度,这是我可以缩短到的最短时间):
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
)或以某种方式更改我的体系结构。有什么方法可以获取我想要的调用语义?