2009-04-29 6 views
2

Ich versuche, einen Teil meines Codes flüssiger zu machen.Erhalten einer parameterlosen Methode, um wie ein Func zu handeln <ReturnT>

Ich habe eine Zeichenfolge-Erweiterung, die eine HTTP-Anforderung aus der Zeichenfolge und die Antwort als Zeichenfolge zurückgibt. So kann ich so etwas wie ...

string _html = "http://www.stackoverflow.com".Request(); 

Ich versuche, eine Erweiterung zu schreiben, der die Anforderung halten versuchen, bis es gelingt. Meine Unterschrift sieht so etwas wie ...

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 
} 

Ich beabsichtige, es zu nennen so etwas wie ...

string _html = "http://www.stackoverflow.com".Request.KeepTrying(); 

Ach, die nicht zu funktionieren scheint =). Ich habe versucht, es zuerst zu einem Lambda zu machen, aber das scheint auch nicht zu funktionieren.

Gibt es eine Möglichkeit zu tun, was ich versuche, während die Syntax ziemlich flüssig bleibt? Vorschläge sehr geschätzt.

Danke.

Antwort

4

Sie können keine Methodengruppe für Erweiterungsmethoden oder Lambda-Ausdrücke verwenden. I blogged about this a while ago.

Ich vermute, Sie Func<string> werfen könnte:

string _html = ((Func<string>)"http://www.stackoverflow.com".Request) 
        .KeepTrying(); 

aber das ist ziemlich böse.

Eine Alternative wäre Request()-Rückkehr zu ändern ein Func und verwenden:

string _html = "http://www.stackoverflow.com".Request().KeepTrying(); 

Oder wenn Sie das Request Methode selbst einfach, fügen Sie einfach eine RequestFunc Methode halten wollte:

public static Func<string> RequestFunc(this string url) 
{ 
    return() => url.Request(); 
} 

und dann rufen:

string _html = "http://www.stackoverflow.com".RequestFunc().KeepTrying(); 
3

Warum nicht auf den Kopf stellen?

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()); 
+0

Darn - Ich hatte gehofft, dass du es als var schreiben könnte html = KeepTrying ("..." anfordern.), Aber Methodengruppen und generische Art Inferenz nicht zusammen sehr gut spielen :( –

+0

Das war meine letzte Option. – Fung

+0

@ Sambo99 - Ich denke, es wird wirklich meine letzte Option sein. Grund ist für lange Lambdas, das Ende der KeepTrying wird durch die schließende Klammer begrenzt, die manchmal etwas schwieriger zu erkennen ist, zB Ke epTrying (() => _x.DoSomething(). DoMoreStuff(). AndThenSomeMore(). PlusSomeOfThisStuff()). SubString (0, 5); Habe trotzdem aufgewertet. Ich werde den letzten Vorschlag von Skeet mit einigen Funktionskettenhelfern verwenden. – Fung

1

Was ist mit der Verbesserung der Anfrage?

string _html = "http://www.stackoverflow.com".Request(RequestOptions.KeepTrying); 

string _html = "http://www.stackoverflow.com".Request(RequestOptions.Once); 

RequestOptions ist ein enum. Sie könnten auch mehr Optionen, Timeout-Argumente, Anzahl der Wiederholungen usw. haben.

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(); 
+0

Als generische Methode KeepTrying könnte auf alle Func , nicht nur Request angewendet werden. Auch meine Meinung ist, dass die Verwendung von Enums ein wenig aus der Geläufigkeit herausnimmt. – Fung

+0

Das ist schön, solange KeepTrying auf einem Request keine spezielle Behandlung benötigt (zB Exceptions) und zusätzliche Argumente (Timeout etc). –

0

AFAIK können Sie eine Erweiterungsmethode schreiben, die eine Func<T> Delegierten erstreckt, aber der Compiler nicht weiß, was meinen Sie:

string _html = "http://www.stackoverflow.com".Request.KeepTrying(); // won't work 

Aber wenn Sie explizit die Guss Delegierter wird arbeiten:

string _html = ((Func<string>)"http://www.stackoverflow.com".Request).KeepTrying(); // works 

Die Frage hier, ob der Code readabil In diesem Fall wird die Funktionalität durch eine Erweiterungsmethode verbessert.

0

Ich würde keine Erweiterungsmethode für Zeichenfolge schreiben. Verwenden Sie einen spezifischeren Typ, z. B. Uri.

Der vollständige 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(); 
    }  
} 

Nannte es:

string html = new Uri("http://www.stackoverflow.com").Request().KeepTrying(); 
Verwandte Themen