Получение метода без параметров, который будет действовать как Func<ReturnT>
-
03-07-2019 - |
Вопрос
Я пытаюсь сделать часть своего кода более гибкой.
У меня есть расширение строки, которое создает HTTP-запрос из строки и возвращает ответ в виде строки.Поэтому я могу сделать что-то вроде...
string _html = "http://www.stackoverflow.com".Request();
Я пытаюсь написать расширение, которое будет продолжать выполнять запрос до тех пор, пока он не будет успешным.Моя подпись выглядит примерно так...
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
}
Я собираюсь назвать это как-то так...
string _html = "http://www.stackoverflow.com".Request.KeepTrying();
Увы, это не работает =).Сначала я попытался превратить это в лямбду, но, похоже, это тоже не сработало.
string _html = (() => "http://www.stackoverflow.com".Request()).KeepTrying();
Есть ли способ сделать то, что я пытаюсь сделать, сохраняя при этом синтаксис достаточно свободным?Предложения очень ценятся.
Спасибо.
Решение
Вы не можете использовать группу методов для методов расширения или лямбда-выражений.я писал об этом в блоге некоторое время назад.
Я подозреваю, что вы могли бы использовать Func<string>
:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request)
.KeepTrying();
но это довольно противно.
Альтернативой может быть изменение Request()
к возвращаться Func и используйте:
string _html = "http://www.stackoverflow.com".Request().KeepTrying();
Или, если вы хотите сохранить Request
сам метод прост, просто добавьте RequestFunc
метод:
public static Func<string> RequestFunc(this string url)
{
return () => url.Request();
}
а затем позвоните:
string _html = "http://www.stackoverflow.com".RequestFunc().KeepTrying();
Другие советы
Почему бы не перевернуть это с ног на голову?
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());
А как насчет улучшения запроса?
string _html = "http://www.stackoverflow.com".Request(RequestOptions.KeepTrying);
string _html = "http://www.stackoverflow.com".Request(RequestOptions.Once);
RequestOptions
является перечислением.У вас также может быть больше опций, аргументов тайм-аута, количества повторов и т. д.
ИЛИ
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, вы можете написать метод расширения, который расширяет Func<T>
делегат, но компилятор не знает, что вы имеете в виду:
string _html = "http://www.stackoverflow.com".Request.KeepTrying(); // won't work
Но если вы явно приведёте делегата, он будет работать:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request).KeepTrying(); // works
Вопрос здесь в том, действительно ли в данном случае методом расширения улучшается читаемость кода.
Я бы не стал писать метод расширения для строки.Используйте более конкретный тип, например Uri
.
Полный код:
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();
}
}
Вызов этого:
string html = new Uri("http://www.stackoverflow.com").Request().KeepTrying();