Okay, lassen Sie mich die Szene setzen: Wir haben eine Funktion in unserem Code verwendet, die eine Funktion und macht einige Logging um es herum und gibt dann das Ergebnis zurück . Es sieht ein bisschen so aus.Für Func <T, TResult>, wo A verlängert T, A erfüllt nicht für T
TResponse LoggedApiCall<TResponse>(Func<BaseRequest, BaseResponse> apiCall, ...)
where TResponse : BaseResponse;
Im Gebrauch mit dieser habe ich die vier folgenden Objekte
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 {
...
}
}
Also, mit diesem I Mock LoggedApiCall (mit Moq), um einige Unit-Tests zu unterstützen versuchen. Ich schreibe eine generische Methode, die es uns ermöglicht, eine Funktion übergeben, die die Basistyp-Einschränkungen erfüllt, und eine Antwort, die auch Übereinstimmungen eingibt, um eine allgemeine Methode zu erstellen, um .Setup() auf dem Mock auszuführen.
Es sieht wie folgt aus:
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);
}
}
Der Grund, warum ich die Funktion zu werfen versucht bin, ist, dass, wenn ich nicht tun, erhalte ich die Intellisense Fehler
Argument type 'System.Func<TRequest, TResponse>' is not assignable to
parameter type 'System.Func<Name.Space.Base.BaseRequest, Name.Space.Base.BaseResponse>'
Dieses ich etwas finden das verwirrend ist, da TRequest und TResponse wie durch BaseRequest und BaseResponse jeweils eingeschränkt werden, aber wenn ich umgehen muss, werde ich. Wenn jedoch die Besetzung
var baseFunction = function as Func<BaseRequest, BaseResponse>
Durchführung löst es als null. Dies finde ich auch verwirrend aufgrund der oben erwähnten Einschränkungen für den Parameter, der an SetupLoggedApiCall übergeben wurde. Ich habe einige graben weiter, während das Debuggen des Codes und bekam die folgende:
function is Func<TRequest, TResponse> | true
function is Func<TRequest, BaseResponse> | true
function is Func<BaseRequest, BaseResponse> | false
Wie diese zeigt, setzt tAntwort BaseResponse gerecht zu werden und kann ohne Probleme auf sie geworfen werden. Sobald wir jedoch versuchen, von TRequest zu BaseRequest zu wechseln, schlägt es fehl. Nur um sicherzustellen, dass ich nicht in eine Situation bekommen, wo keine falschen Arten importiert oder irgendetwas folgte ich diese uns mit:
typeof(TRequest).BaseType == typeof(BaseRequest) | true
Also, kann mir jemand sagen: Da alles auf TRequest eine BaseRequest da dies Cast scheitert an der Frage von TRequest?
Wir werden anfangen, dies zu entfernen und das Problem in einem neuen Code-Projekt zu isolieren (in Wirklichkeit ist unser Code nicht ganz so einfach wie unten, ich habe ihn vereinfacht) und sehe, an welchem Punkt es scheitert und wir werden aktualisieren, wenn wir etwas finden, aber jede Einsicht würde geschätzt werden.
Update 1
Nach dem Vorschlag folgenden @EugenePodskal ich die Definition von LoggedApiCall aktualisiert zu lesen
TResponse LoggedApiCall<TRequest, TResponse>(Func<TRequest, TResponse> apiCall, ...)
where TRequest : BaseRequest where TResponse : BaseResponse
Das machte SetupLoggedApiCall glücklich, zumindest zum Zeitpunkt der Kompilierung, in dem, was sein war Eingereicht würde jedoch gültig sein, der Aufruf an den verspotteten Dienst gibt immer noch null zurück. Ich grub wieder in das Proxy-Objekt und bis auf das Interceptor für diesen Anruf und ich entdecken diese:
IService service => service.LoggedApiCall<Request, Response>(, /*other params*/)
dass kein Tippfehler ist. Der erste Parameter fehlt einfach im Interzeptor.Ich schätze, das verlagert die Frage mehr auf Mock als auf Func, aber wenn man sieht, dass der Interceptor ein Lamba-Ausdruck ist, kann jeder etwas erhellen, was dazu führen könnte, dass dieser Parameter einfach fehlt.
Ich sehe nicht, wie dies ein Duplikat ist - die verknüpfte Frage ist eine Erläuterung einiger Details bezüglich der Varianz von 'Func', während diese Frage tatsächlich eine Einführung/Erklärung der Varianz im Allgemeinen erfordert. Der Kontext ist der gleiche, aber die Frage ist nicht. Die Antwort auf die andere Frage wird dem Fragesteller wahrscheinlich nicht helfen. –
@AntP Die Frage verbunden ist, "warum kann ich nicht" Aktion 'zu' Aktion '", und diese Frage im Wesentlichen fragen "Warum kann ich nicht' Func '' 'Func ' ". –
Rawling
@Rawling Sicher, wenn Sie Dinge vereinfachen möchten. Die andere Frage und ihre Antworten nehmen bereits das Verständnis des Begriffs der Varianz an, so dass die Antworten auf diese Frage nutzlos sind, wenn sie auf diesen angewendet werden. Z.B. "Warum kann ich nicht' Func '' 'Func ' 'kann nicht richtig mit" Sie haben die Kovarianz und Kontravarianz den falschen Weg. " –