Хорошо, позвольте мне установить сцену: У нас есть функция, используемая в нашем коде, которая принимает функцию и выполняет некоторую запись по ней, а затем возвращает результат , Это выглядит немного похоже на это.Для Func <T, TResult>, где A extends T, A не удовлетворяет для T
TResponse LoggedApiCall<TResponse>(Func<BaseRequest, BaseResponse> apiCall, ...)
where TResponse : BaseResponse;
При использовании с этим у меня есть четыре следующих объектов
namespace Name.Space.Base {
public class BaseRequest {
...
}
}
namespace Name.Space.Base {
public class BaseResponse {
...
}
}
namespace Some.Other.Name.Space {
public class Request : BaseRequest {
...
}
}
namespace Name.Space {
public class Response<TPayload> : BaseResponse {
...
}
}
Итак, с этим я пытаюсь макет LoggedApiCall (используя Moq) для поддержки некоторых модульных тестов. Я пишу общий метод, который позволяет нам передать функцию, которая соответствует ограничениям базового типа, и ответ, который также набирает совпадения для создания общего метода для выполнения .Setup() в Mock.
Это выглядит следующим образом:
protected IReturnsResult<IService> SetupLoggedApiCall<TRequest, TResponse>(
Func<TRequest, TResponse> function,
TResponse response
)
where TRequest : BaseRequest
where TResponse : BaseResponse
{
var baseFunction = function as Func<BaseRequest, BaseResponse>;
return _mockService.Setup(service => service.LoggedApiCall<TResponse>(
baseFunction, /*other parameters *
))
.Returns(response);
}
}
Поэтому я пытаюсь бросить функцию в том, что если у меня нет, я получаю ошибку INTELLISENSE
Argument type 'System.Func<TRequest, TResponse>' is not assignable to
parameter type 'System.Func<Name.Space.Base.BaseRequest, Name.Space.Base.BaseResponse>'
Это, я считаю, немного поскольку TRequest и TResponse ограничены как BaseRequest, так и BaseResponse соответственно, но если я буду работать, я должен. Однако при выполнении бросания
var baseFunction = function as Func<BaseRequest, BaseResponse>
он решает, как нуль. Это, я также считаю, из-за вышеупомянутых ограничений на параметр, переданный в SetupLoggedApiCall. я сделал некоторые дополнительные рыть, пока отладки кода и получил следующее:
function is Func<TRequest, TResponse> | true
function is Func<TRequest, BaseResponse> | true
function is Func<BaseRequest, BaseResponse> | false
, так как это показывает, TResponse продолжает удовлетворять BaseResponse и может быть приведен к нему без каких-либо проблем. Однако, как только мы попытаемся перейти из TRequest в BaseRequest, это не сработает. Просто чтобы убедиться, что я не получал в ситуации, когда импортировала любые неправильные типы или все, что я следовал за этим с нами:
typeof(TRequest).BaseType == typeof(BaseRequest) | true
Так, может кто-нибудь сказать мне: Учитывая, что все указывает на TRequest быть BaseRequest это бросает неудачу в вопросе TRequest?
Мы собираемся начать эту работу и изолировать проблему в новом проекте кода (на самом деле наш код не так прост, как он ниже, я упростил его до самого ядра) и посмотрите, в какой точке он терпит неудачу, и мы будем обновляться, если найдем что-либо, но любое понимание будет оценено по достоинству.
Update 1
По вытекающее из предложения @EugenePodskal Я обновил определение LoggedApiCall читать
TResponse LoggedApiCall<TRequest, TResponse>(Func<TRequest, TResponse> apiCall, ...)
where TRequest : BaseRequest where TResponse : BaseResponse
Это сделало SetupLoggedApiCall счастливым, по крайней мере, во время компиляции, в том, что в настоящее время однако, он будет действителен, но вызов обманутой службы все равно возвращает null. Я вырыл назад в прокси-объект и вниз к перехватчик для этого вызова, и я обнаружил это:
IService service => service.LoggedApiCall<Request, Response>(, /*other params*/)
Это не опечатка. Первый параметр просто отсутствует у перехватчика.Я предполагаю, что этот вопрос больше переключается на то, что он относится к Mock, чем к Func, но, увидев, что перехватчик является выражением lamba, кто может пролить свет на то, что может привести к тому, что этот параметр просто отсутствует?
Я не вижу, как это дубликат. Связанный вопрос - это разъяснение некоторых деталей относительно дисперсии 'Func', в то время как этот вопрос фактически требует введения/объяснения дисперсии в целом. Контекст тот же, но вопрос не таков. Ответ на другой вопрос, скорее всего, не поможет об этом. –
@AntP Связанный с этим вопрос: «Почему я не могу наложить действие« 'на« Действие '", и этот вопрос по существу задает вопрос: «Почему я не могу использовать' Func '' 'Func ' ". –
Rawling
@ Rawling Конечно, если вам нравится упрощать вещи. Другой вопрос и его ответы уже предполагают понимание концепции дисперсии, поэтому ответы на этот вопрос бесполезны при применении к этому. Например. «Почему я не могу наложить' Func 'на' Func '' «не может быть правильно ответил с« У вас есть ковариация и контравариантность неправильным образом ». –