Могу ли я помочь компилятору C # вывести этот тип? - PullRequest
0 голосов
/ 19 ноября 2018

У меня проблема с выводом типа и компилятором 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) или каким-либо образом изменить мою архитектуру. Есть ли способ получить семантику вызова, которую я хочу?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...