Getting a parameterless method to act like a Func<ReturnT>
-
03-07-2019 - |
Question
I'm trying to make a part of my code more fluent.
I have a string extension that makes an HTTP request out of the string and returns the response as a string. So I can do something like...
string _html = "http://www.stackoverflow.com".Request();
I'm trying to write an extension that will keep trying the request until it succeeds. My signature looks something like...
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
}
I intend to call it something like...
string _html = "http://www.stackoverflow.com".Request.KeepTrying();
Alas, that doesn't seem to work =). I tried making it into a lambda first but that doesn't seem to work either.
string _html = (() => "http://www.stackoverflow.com".Request()).KeepTrying();
Is there a way to do what I'm trying to do while keeping the syntax fairly fluent? Suggestions much appreciated.
Thanks.
Solution
You can't use a method group for extension methods, or lambda expressions. I blogged about this a while ago.
I suspect you could cast to Func<string>
:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request)
.KeepTrying();
but that's pretty nasty.
One alternative would be to change Request()
to return a Func, and use:
string _html = "http://www.stackoverflow.com".Request().KeepTrying();
Or if you wanted to keep the Request
method itself simple, just add a RequestFunc
method:
public static Func<string> RequestFunc(this string url)
{
return () => url.Request();
}
and then call:
string _html = "http://www.stackoverflow.com".RequestFunc().KeepTrying();
OTHER TIPS
Why not turn this on its head?
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());
What about enhancing the Request?
string _html = "http://www.stackoverflow.com".Request(RequestOptions.KeepTrying);
string _html = "http://www.stackoverflow.com".Request(RequestOptions.Once);
RequestOptions
is a enum. You could also have more options, timeout arguments, number of retries etc.
OR
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 you can write an extension method that extends a Func<T>
delegate, but the compiler doesn't know what do you mean:
string _html = "http://www.stackoverflow.com".Request.KeepTrying(); // won't work
But if you explicitly cast the delegate will work:
string _html = ((Func<string>)"http://www.stackoverflow.com".Request).KeepTrying(); // works
The question here it whether the code readability is really improved in this case by an extension method.
I wouldn't write an extension method for string. Use a more specific type, like the Uri
.
The full code:
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();
}
}
Calling it:
string html = new Uri("http://www.stackoverflow.com").Request().KeepTrying();