Far sì che un metodo senza parametri si comporti come un Func < ReturnT >
-
03-07-2019 - |
Domanda
Sto cercando di rendere più fluente una parte del mio codice.
Ho un'estensione di stringa che crea una richiesta HTTP dalla stringa e restituisce la risposta come stringa. Quindi posso fare qualcosa come ...
string _html = "http://www.stackoverflow.com".Request();
Sto cercando di scrivere un'estensione che continuerà a provare la richiesta fino a quando non avrà esito positivo. La mia firma assomiglia a ...
public static T KeepTrying<T>(this Func<T> KeepTryingThis) {
// Code to ignore exceptions and keep trying goes here
// Returns the result of KeepTryingThis if it succeeds
}
Ho intenzione di chiamarlo in qualche modo come ...
string _html = "http://www.stackoverflow.com".Request.KeepTrying();
Purtroppo, sembra che non funzioni =). Ho provato prima a trasformarlo in una lambda, ma non sembra funzionare neanche.
string _html = (() => "http://www.stackoverflow.com".Request()).KeepTrying();
C'è un modo per fare quello che sto cercando di fare mantenendo la sintassi abbastanza fluente? Suggerimenti molto apprezzati.
Grazie.
Soluzione
Non puoi usare un gruppo di metodi per metodi di estensione o espressioni lambda. I ha scritto un blog su questo argomento .
Sospetto che potresti trasmettere a Func < string >
:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request)
.KeepTrying();
ma è piuttosto brutto.
Un'alternativa sarebbe cambiare Request ()
in return a Func e usare:
string _html = "http://www.stackoverflow.com".Request().KeepTrying();
O se vuoi mantenere semplice il metodo Request
, aggiungi semplicemente un metodo RequestFunc
:
public static Func<string> RequestFunc(this string url)
{
return () => url.Request();
}
e quindi chiama:
string _html = "http://www.stackoverflow.com".RequestFunc().KeepTrying();
Altri suggerimenti
Perché non capovolgerlo?
static T KeepTrying<T>(Func<T> func) {
T val = default(T);
while (true) {
try {
val = func();
break;
} catch { }
}
return val;
}
var html = KeepTrying(() => "http://www.stackoverflow.com".Request());
Che dire di migliorare la richiesta?
string _html = "http://www.stackoverflow.com".Request(RequestOptions.KeepTrying);
string _html = "http://www.stackoverflow.com".Request(RequestOptions.Once);
RequestOptions
è un enum. Potresti anche avere più opzioni, argomenti di timeout, numero di tentativi ecc.
o
public static string RepeatingRequest(this string url) {
string response = null;
while ( response != null /* how ever */ ) {
response = url.Request();
}
return response;
}
string _html = "http://www.stackoverflow.com".RepeatingRequest();
AFAIK puoi scrivere un metodo di estensione che estende un delegato Func < T >
, ma il compilatore non sa cosa vuoi dire:
string _html = "http://www.stackoverflow.com".Request.KeepTrying(); // won't work
Ma se lanci esplicitamente il delegato funzionerà:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request).KeepTrying(); // works
La domanda qui è se la leggibilità del codice è davvero migliorata in questo caso da un metodo di estensione.
Non scriverei un metodo di estensione per stringa. Usa un tipo più specifico, come Uri
.
Il codice completo:
public static class Extensions
{
public static UriRequest Request(this Uri uri)
{
return new UriRequest(uri);
}
public static UriRequest KeepTrying(this UriRequest uriRequest)
{
uriRequest.KeepTrying = true;
return uriRequest;
}
}
public class UriRequest
{
public Uri Uri { get; set; }
public bool KeepTrying { get; set; }
public UriRequest(Uri uri)
{
this.Uri = uri;
}
public string ToHtml()
{
var client = new System.Net.WebClient();
do
{
try
{
using (var reader = new StreamReader(client.OpenRead(this.Uri)))
{
return reader.ReadToEnd();
}
}
catch (WebException ex)
{
// log ex
}
}
while (KeepTrying);
return null;
}
public static implicit operator string(UriRequest uriRequest)
{
return uriRequest.ToHtml();
}
}
Chiamandolo:
string html = new Uri("http://www.stackoverflow.com").Request().KeepTrying();