Un delegato C# può utilizzare il tipo di oggetto per essere più generico?
-
09-09-2019 - |
Domanda
Vorrei creare un delegato e un metodo che possa essere utilizzato per chiamare qualsiasi numero di servizi Web richiesti dalla mia applicazione:
Esempio:
public DateCheckResponseGetDate(DateCheckRequest requestParameters)
{
delegate object WebMethodToCall(object methodObject);
WebMethodToCall getTheDate = new WebMethodToCall(WebServices.GetTheDate);
return (DateCheckResponse)CallWebMethod(getTheDate , requestParameters);
}
public TimeCheckResponse GetTime(TimeCheckRequest requestParameters)
{
delegate object WebMethodToCall(object methodObject);
WebMethodToCall getTheTime = new WebMethodToCall(WebServices.GetTheTime);
return (TimeCheckResponse)CallWebMethod(getTheTime, requestParameters);
}
private object CallWebMethod(WebMethodToCall method, object methodObject)
{
return method(methodObject);
}
Ma, sfortunatamente, quando provo a compilare, ottengo questi errori:
Nessun sovraccarico per "gettheDate" abbina delegate "webmethodtocall" Nessun sovraccarico per "getTheTime" abbina il delegato "WebMethodTocall"
Sembra che il delegato dovrebbe funzionare.
WebServices.GetTheDate e WebServices.GetTheTime accettano entrambi un singolo parametro (DateCheckRequest e TimeCheckRequest, rispettivamente) ed entrambi restituiscono un valore.
Quindi il delegato non corrisponde alla firma dei due metodi web?(entrambi accettano e restituiscono tipi derivati dall'oggetto).
È possibile utilizzare il tipo di oggetto per creare un delegato molto riutilizzabile in .NET 2.0?
Soluzione
Ti suggerisco di utilizzare un delegato generico come Func<T, TResult>
:
public DateCheckResponseGetDate(DateCheckRequest requestParameters)
{
// Split declaration/assignment just to avoid wrapping
Func<DateCheckRequest, DateCheckResponse> method;
method = WebServices.GetTheDate;
return CallWebMethod(method, requestParameters);
}
Allora faresti CallWebMethod
anche generico:
public TResponse CallWebMethod<TRequest, TResponse>
(Func<TRequest, TResponse> method, TRequest request)
{
// Whatever you do in here.
}
Altri suggerimenti
Ti suggerirei di modificare il codice in qualcosa del tipo:
public DateCheckResponseGetDate(DateCheckRequest requestParameters)
{
Func<DateCheckRequest, DateCheckResponse> getTheDate = new Func<DateCheckRequest, DateCheckResponse>(WebServices.GetTheDate);
return CallWebMethod(getTheDate , requestParameters);
}
//DEFINE CallWebMethod ADEQUATELY!
public T CallWebMethod<T,U> (Func<T,U> webMethod, U arg)
{
return webMethod(arg);
}
In questo modo puoi evitare tutti i brutti downcasting :)
L'ho risolto grazie ai commenti qui.
private delegate object WebMethodToCall<T>(T methodObject);
public DateCheckResponseGetDate(DateCheckRequest requestParameters)
{
WebMethodToCall<DateCheckRequest> getTheDate = new WebMethodToCall<DateCheckRequest>(WebServices.GetTheDate);
return CallWebMethod<DateCheckResponse, DateCheckRequest>(getTheDate, requestParameters);
}
public TimeCheckResponse GetTime(TimeCheckRequest requestParameters)
{
WebMethodToCall<TimeCheckRequest> getTheTime = new WebMethodToCall<TimeCheckRequest>(WebServices.GetTheTime);
return CallWebMethod<TimeCheckResponse, TimeCheckRequest>(getTheTime, requestParameters);
}
private T CallWebMethod<T, U>(WebMethodToCall<U> method, U methodObject)
{
return (T)method(methodObject);
}
Sì, ma dovresti riscrivere WebServices.GetTheData e GetTheTime per accettare oggetti o fornire sovraccarichi che accettano oggetti.
Questo perché non puoi upcast oggetti in .NET in modo implicito.In altre parole, puoi fare questo:
TimeCheckRequest myTimeCheckRequest;
object foo = myTimeCheckRequest;
ma non puoi farlo:
object myTimeCheckRequest;
TimeCheckRequest foo = myTimeCheckRequest;
Puoi definire Func come:
public delegate TResult Func<T, TResult>(T arg);
Questa definizione non richiede nulla che non sia disponibile in .NET 2.0 e aggiunta allo snipped che ho pubblicato sopra risolve il tuo problema :)
AGGIORNAMENTO: Potrebbe non essere più valido?Non lo so, l'ho fatto in C# 2.0
Per espandere la risposta di Will, mentre .NET non lo farà implicitamente per te, puoi forzarlo aggiungendo un operatore implicito alla tua classe TimeCheckRequest:
public class TimeCheckRequest
{
...
public static implicit operator TimeCheckRequest(object request)
{
return (TimeCheckRequest) request;
}
}
Vorrei non lo consiglio, tuttavia, poiché è possibile utilizzare a delegato generico invece senza il costo di esecuzione di tutto il casting.