Va bene, mi permetta di impostare la scena: Abbiamo una funzione utilizzata all'interno del nostro codice che prende una funzione e fa qualche registrazione intorno ad esso e poi restituisce il risultato . Sembra un po 'qualcosa di simile.Per Func <T, TResult>, dove A estende T, A non soddisfa per T
TResponse LoggedApiCall<TResponse>(Func<BaseRequest, BaseResponse> apiCall, ...)
where TResponse : BaseResponse;
In uso con questo ho le seguenti quattro oggetti
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 {
...
}
}
Così, con questi che sto cercando LoggedApiCall finto (utilizzando Moq) al fine di supportare alcune unit test. Sto scrivendo un metodo generico che ci consente di passare una funzione che soddisfa i vincoli del tipo di base e una risposta che digita anche le corrispondenze per creare un metodo comune per eseguire .Setup() sul Mock.
Ecco come si presenta:
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);
}
}
Il motivo per cui sto cercando di lanciare la funzione è che se non lo faccio, ottengo l'errore intellisense
Argument type 'System.Func<TRequest, TResponse>' is not assignable to
parameter type 'System.Func<Name.Space.Base.BaseRequest, Name.Space.Base.BaseResponse>'
Questo, trovo un po ' bemusing come TRequest e TResponse sono vincolati come da BaseRequest e BaseResponse rispettivamente, ma se lavoro intorno devo, lo farò. Tuttavia, quando si esegue il cast
var baseFunction = function as Func<BaseRequest, BaseResponse>
si risolve come nullo. Questo, trovo anche bemusing a causa dei vincoli sopra menzionati sul parametro passato in SetupLoggedApiCall. Ho fatto qualche ulteriore scavo, mentre il debug del codice e ha ottenuto la seguente:
function is Func<TRequest, TResponse> | true
function is Func<TRequest, BaseResponse> | true
function is Func<BaseRequest, BaseResponse> | false
Come illustra la presente, tRisposta continua a soddisfare BaseResponse e può essere gettato ad esso senza problemi. Tuttavia, non appena proviamo a passare da TRequest a BaseRequest, fallisce. Giusto per assicurarsi che io non ero sempre in una situazione in cui importato alcun tipo sbagliato o qualcosa Ho seguito questo noi con:
typeof(TRequest).BaseType == typeof(BaseRequest) | true
Così, qualcuno può dirmi: Dato che tutto fa pensare TRequest essere un BaseRequest questo il cast fallisce in merito a TREGEST?
Stiamo per iniziare a rimuovere questo problema e isolare il problema in un nuovo progetto di codice (in realtà, il nostro codice non è così semplice come lo è in basso, l'ho semplificato al suo nucleo) e vediamo a quale punto fallisce e aggiorneremo se troviamo qualsiasi cosa, ma qualsiasi intuizione sarebbe apprezzata.
Update 1
Al seguito dalla suggestione @EugenePodskal Ho aggiornato la definizione di LoggedApiCall leggere
TResponse LoggedApiCall<TRequest, TResponse>(Func<TRequest, TResponse> apiCall, ...)
where TRequest : BaseRequest where TResponse : BaseResponse
Questo fatto SetupLoggedApiCall felice, almeno al momento della compilazione, in quanto ciò che è stato passato in sarebbe valido tuttavia, la chiamata al servizio deriso restituisce ancora null. Ho scavato nuovamente dentro l'oggetto proxy e giù per l'intercettore per questa chiamata e ho scoperto questo:
IService service => service.LoggedApiCall<Request, Response>(, /*other params*/)
Questo non è un errore di battitura. Il primo parametro semplicemente manca dall'intercettore.Immagino che questo cambi la domanda di essere più su Mock che su Func, ma visto che l'intercettatore è un'espressione di lamba chiunque sia in grado di far luce su ciò che potrebbe causare che il paramater semplicemente manchi?
Non vedo come questo sia un duplicato - la domanda collegata è un chiarimento di alcuni dettagli riguardanti la varianza di 'Func' mentre questa domanda richiede in realtà un'introduzione/spiegazione della varianza in generale. Il contesto è lo stesso, ma la domanda non lo è. La risposta all'altra domanda probabilmente non aiuterà il richiedente di questo. –
@AntP La domanda collegata è "perché non posso lanciare l'azione' nell'azione '", e questa domanda è essenzialmente chiedendo "perché non posso trasmettere' Func 'a Func ' ". –
Rawling
@Rawling Certo, se ti piace semplificare eccessivamente le cose. L'altra domanda e le sue risposte presuppongono già la comprensione del concetto di varianza, quindi le risposte a questa domanda sono inutili se applicate a questo. Per esempio. "Perché non posso trasmettere' Func 'a' Func '" non si può rispondere correttamente con "Hai la covarianza e la controvarianza nel modo sbagliato". –