У меня много веселья Funcy (предназначено для забавы) с помощью общих методов. В большинстве случаев вывод типа C # достаточно умен, чтобы выяснить, какие универсальные аргументы он должен использовать в моих универсальных методах, но теперь у меня есть проект, в котором компилятор C # не работает, хотя я считаю, что он мог бы успешно найти правильные типы.
Может кто-нибудь сказать мне, является ли компилятор немного глупым в этом случае, или есть очень четкая причина, почему он не может вывести мои общие аргументы?
Вот код:
Классы и определения интерфейса:
interface IQuery<TResult> { }
interface IQueryProcessor
{
TResult Process<TQuery, TResult>(TQuery query)
where TQuery : IQuery<TResult>;
}
class SomeQuery : IQuery<string>
{
}
Некоторый код, который не компилируется:
class Test
{
void Test(IQueryProcessor p)
{
var query = new SomeQuery();
// Does not compile :-(
p.Process(query);
// Must explicitly write all arguments
p.Process<SomeQuery, string>(query);
}
}
Почему это? Что мне здесь не хватает?
Вот сообщение об ошибке компилятора (оно не оставляет много для нашего воображения):
Аргументы типа для метода IQueryProcessor.Process (TQuery) не могут быть выведены из использования. Попробуйте указать
введите аргументы явно.
Причина, по которой я считаю, что C # должен иметь возможность сделать вывод, заключается в следующем:
- Я предоставляю объект, который реализует
IQuery<TResult>
.
- Это только версия
IQuery<TResult>
, которую реализует тип: IQuery<string>
и, следовательно, TResult должен быть string
.
- С этой информацией у компилятора есть TResult и TQuery.
РЕШЕНИЕ
Для меня лучшим решением было изменить интерфейс IQueryProcessor
и использовать динамическую типизацию в реализации:
public interface IQueryProcessor
{
TResult Process<TResult>(IQuery<TResult> query);
}
// Implementation
sealed class QueryProcessor : IQueryProcessor {
private readonly Container container;
public QueryProcessor(Container container) {
this.container = container;
}
public TResult Process<TResult>(IQuery<TResult> query) {
var handlerType =
typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
dynamic handler = container.GetInstance(handlerType);
return handler.Handle((dynamic)query);
}
}
Интерфейс IQueryProcessor
теперь принимает параметр IQuery<TResult>
. Таким образом, он может вернуть TResult
, и это решит проблемы с точки зрения потребителя. Нам нужно использовать отражение в реализации, чтобы получить реальную реализацию, поскольку нужны конкретные типы запросов (в моем случае). Но тут на помощь приходит динамическая типизация, которая сделает отражение для нас. Вы можете прочитать больше об этом в этой статье .