2014-10-21 7 views
9

Хорошо, позвольте мне установить сцену: У нас есть функция, используемая в нашем коде, которая принимает функцию и выполняет некоторую запись по ней, а затем возвращает результат , Это выглядит немного похоже на это.Для 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, кто может пролить свет на то, что может привести к тому, что этот параметр просто отсутствует?

+1

Я не вижу, как это дубликат. Связанный вопрос - это разъяснение некоторых деталей относительно дисперсии 'Func', в то время как этот вопрос фактически требует введения/объяснения дисперсии в целом. Контекст тот же, но вопрос не таков. Ответ на другой вопрос, скорее всего, не поможет об этом. –

+0

@AntP Связанный с этим вопрос: «Почему я не могу наложить действие« 'на« Действие '", и этот вопрос по существу задает вопрос: «Почему я не могу использовать' Func '' 'Func ' ". – Rawling

+1

@ Rawling Конечно, если вам нравится упрощать вещи. Другой вопрос и его ответы уже предполагают понимание концепции дисперсии, поэтому ответы на этот вопрос бесполезны при применении к этому. Например. «Почему я не могу наложить' Func 'на' Func '' «не может быть правильно ответил с« У вас есть ковариация и контравариантность неправильным образом ». –

ответ

1

тема вы должны смотреть на это Covariance and Contravariance in Generics

http://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx

т.е. «В .NET Framework 4, общие делегаты Func, такие как Func, ковариативности возвращаемые типы и контравариантные типы параметров.»

Логично, а также, если вы об этом думаете ..

Func<Apple, AppleProduct> MakeAppleProduct = new Func....; 

// Assume the cast is allowed at runtime and doesn't throw. 
Func<Fruit, FruitProduct> MakeFruitProduct = (Func<Fruit, FruitProduct>) MakeAppleProduct; 

//Returns an instance of AppleProduct 
MakeFruitProduct(appleInstance); 

//Orange is also a Fruit, and hence we are allowed to pass it? Should it be allowed? 
MakeFruitProduct(orangeInstance); 

Таким образом, для функции параметров, вы не хотите, чтобы на приведение к базовому типу.

С другой стороны, для возвращение значения, если функция была первоначально объявлена ​​вернуть экземпляр AppleProduct, это 100% (типа) с уверенностью сказать, что возвращает экземпляр FuitProduct (базовый класс для AppleProduct)

Смежные вопросы