我可以帮助C#编译器推断这种类型吗?

时间:2018-11-19 12:12:00

标签: c# type-inference

我在类型推断和C#编译器方面遇到问题。阅读this questionthis 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)或以某种方式更改我的体系结构。有什么方法可以获取我想要的调用语义?

0 个答案:

没有答案