Conseguir um método sem parâmetros para agir como um Func
-
03-07-2019 - |
Pergunta
Eu estou tentando fazer uma parte do meu código mais fluente.
Eu tenho uma extensão de cadeia que faz uma solicitação HTTP para fora da cadeia e retorna a resposta como uma string. Então eu posso fazer algo parecido ...
string _html = "http://www.stackoverflow.com".Request();
Eu estou tentando escrever uma extensão que vai continuar tentando o pedido até obter êxito. Minha assinatura é algo como ...
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
}
Eu pretendo chamá-lo de algo como ...
string _html = "http://www.stackoverflow.com".Request.KeepTrying();
Infelizmente, isso não parece trabalho =). Eu tentei fazê-lo em um lambda primeiro, mas isso não parece que quer trabalhar.
string _html = (() => "http://www.stackoverflow.com".Request()).KeepTrying();
Existe uma maneira de fazer o que eu estou tentando fazer, mantendo a sintaxe bastante fluente? Sugestões muito apreciada.
Graças.
Solução
Você não pode usar um grupo de método para métodos de extensão ou expressões lambda. I escrevi sobre isso um tempo atrás.
Eu suspeito que você poderia lançar a Func<string>
:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request)
.KeepTrying();
mas que é bastante desagradável.
Uma alternativa seria a mudança Request()
a retorno a Func e uso:
string _html = "http://www.stackoverflow.com".Request().KeepTrying();
Ou se você queria manter o próprio método Request
simples, basta adicionar um método RequestFunc
:
public static Func<string> RequestFunc(this string url)
{
return () => url.Request();
}
e, em seguida, ligue para:
string _html = "http://www.stackoverflow.com".RequestFunc().KeepTrying();
Outras dicas
Por que não transformar isso em sua cabeça?
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());
Que tal melhorar o Pedido?
string _html = "http://www.stackoverflow.com".Request(RequestOptions.KeepTrying);
string _html = "http://www.stackoverflow.com".Request(RequestOptions.Once);
RequestOptions
é uma enumeração. Você também pode ter mais opções, os argumentos de tempo de espera, número de tentativas etc.
ou
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 você pode escrever um método de extensão que se estende um delegado Func<T>
, mas o compilador não sabe o que quer dizer:
string _html = "http://www.stackoverflow.com".Request.KeepTrying(); // won't work
Mas se você converter explicitamente o delegado vai funcionar:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request).KeepTrying(); // works
A questão aqui se a legibilidade do código é realmente melhorou, neste caso, por um método de extensão.
Eu não iria escrever um método de extensão para string. Use um tipo mais específico, como o Uri
.
O código 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();
}
}
Chamá-lo:
string html = new Uri("http://www.stackoverflow.com").Request().KeepTrying();