2016-05-13 11 views
1

Ich habe eine Methode in meiner Xamarin App, die einige Daten vom Server abruft, und ich benötige die Methode, um entweder die Daten zurückzugeben, wenn die HTTP-Anfrage erfolgreich war, oder eine "Systemfehler" -Zeichenfolge, wenn der Server eine zurückgegeben hat Fehler oder "Internet Connectivity Error" -String, wenn keine Konnektivität vorhanden war.Mehrere Typen zurückgeben

Ich würde gerne wissen, wie kann ich Rückgabetyp, um eine von ihnen zurückgeben. Ich weiß, dass Tuple mehrfach zurückgeben kann. Aber ich glaube beides, und auch nicht beides.

My-Code

public async Task<List<AllReportVM>> GetAllReports(string token, string username) 
    { 
     var httpClient = GetHttpClient(); 
     if (CrossConnectivity.Current.IsConnected) { 
      var response = await httpClient.GetAsync ("getdashboardreports.ashx").ConfigureAwait (false); 

      if (response.IsSuccessStatusCode) { 
       var content = response.Content; 

       string jsonString = await content.ReadAsStringAsync().ConfigureAwait (false); 

       return JsonConvert.DeserializeObject<List<AllReportVM>> (jsonString); 
      } else { 
       return "SystemError"; 
      } 
     } else { 
      return "Internet Connectivity Error"; 

     } 
    } 
+0

haben Sie versucht, mit Generika Typ –

+0

Nein, das können Sie nicht tun. Wenn ein Client sagt "ServerData data = erwarten GetAllReports();", muss der Compiler wissen, dass 'ServerData' der richtige Rückgabetyp ist. Der Compiler kann nicht wissen, ob die Anfrage zur Laufzeit erfolgreich ist oder fehlschlägt! Wenn ich Sie wäre, würde ich eine "Response" -Klasse machen, die alle zurückgegebenen Informationen enthält: Success boolean, ErrorMessage string, Result ServerData. Lassen Sie ErrorMessage einfach null, wenn kein Fehler, etc. * Edit * oder werfen Sie eine Ausnahme, natürlich. – Blorgbeard

Antwort

4

Was Sie verlangen, ist effektiv ein discriminated union. C# hat eigentlich nicht so etwas. Es gibt ein paar Möglichkeiten, dies zu umgehen. Zunächst betrachten, was ich als das try/out Muster:

public bool GetAllResults(string token, string username, out List<AllReportVM> results); 

Das ist in Ordnung für relativ einfache Methoden, aber es nicht wirklich geben Sie die Fehlermeldung (zumindest angesichts der Art und Weise sie in der Regel umgesetzt wird) und out Parameter funktionieren nicht mit async Methoden. Also können wir das ausschließen.

Die zweite Möglichkeit besteht darin, throw an exception auf einen Fehler, und das, was würde ich in 90% der Situationen empfehlen:

public async Task<List<AllReportVM>> GetAllReports(string token, string username) 
{ 
    var httpClient = GetHttpClient(); 
    if (CrossConnectivity.Current.IsConnected) { 
     var response = await httpClient.GetAsync ("getdashboardreports.ashx").ConfigureAwait (false); 

     if (response.IsSuccessStatusCode) { 
      var content = response.Content; 

      string jsonString = await content.ReadAsStringAsync().ConfigureAwait (false); 

      return JsonConvert.DeserializeObject<List<AllReportVM>> (jsonString); 
     } else { 
      throw new RequestException("SystemError"); 
     } 
    } else { 
     throw new RequestException("Internet Connectivity Error"); 
    } 
} 

Und das nennen würden Sie verwenden müssen:

try 
{ 
    var list = await obj.GetAllReports(token, username); 
    ... 
} 
catch (RequestException ex) 
{ 
    Console.WriteLine(ex.Message); 
} 

Dies ist ein ziemlich solides, gut etabliertes Muster. Tatsächlich sollten Sie die Ausnahmebehandlung wahrscheinlich bereits für alle möglichen anderen Ausnahmen verwenden, die möglicherweise in Ihrer Anwendung auftreten. Es hat jedoch einige Auswirkungen auf die Leistung und Sie sollten Ausnahmen für den einfachen Kontrollfluss vermeiden. Dies ist möglicherweise keine Option für Anwendungen, die viele Anfragen stellen müssen und von denen erwartet wird, dass sie Fehler effizient behandeln (z. B. Stapelverarbeitung). Wenn ich Situationen wie dies in der Vergangenheit erlebt habe, habe ich es nützlich finde eine benutzerdefinierte Klasse zu implementieren, zum Beispiel:

public class RequestResult<T> 
{ 
    public bool Success { get; } 
    public T Result { get; } 
    public string ErrorMessage { get; } 

    private RequestResult(bool success, T result, string errorMessage) 
    { 
     this.Success = success; 
     this.Result = result; 
     this.ErrorMessage = errorMessage; 
    } 

    public static RequestResult<T> Success(T result) 
    { 
     return new RequestResult<T>(true, result, null); 
    } 

    public static RequestResult<T> Failure(string errorMessage) 
    { 
     return new RequestResult<T>(false, default(T), errorMessage); 
    } 
} 

public async Task<RequestResult<List<AllReportVM>>> GetAllReports(string token, string username) 
{ 
    var httpClient = GetHttpClient(); 
    if (CrossConnectivity.Current.IsConnected) { 
     var response = await httpClient.GetAsync ("getdashboardreports.ashx").ConfigureAwait (false); 

     if (response.IsSuccessStatusCode) { 
      var content = response.Content; 

      string jsonString = await content.ReadAsStringAsync().ConfigureAwait (false); 

      var result = JsonConvert.DeserializeObject<List<AllReportVM>> (jsonString); 
      return RequestResult.Success(result); 
     } else { 
      return RequestResult.Failure("SystemError"); 
     } 
    } else { 
     return RequestResult.Failure("Internet Connectivity Error"); 
    } 
} 
0

Wenn die einzigen Rückgabetypen anders als erfolgreich Erträge sind Fehler, als Sie eine Ausnahme auslösen sollte. Wenn Sie eine bestimmte Ausnahmegeschichte im Sinn haben, sollten Sie eine Klasse erstellen, die von der Exception-Klasse erbt und diese ausgibt. Wenn eine Ausnahme vorhanden ist, die Ihr erwartetes Problem beschreibt, sollten Sie dies bevorzugen. Die aufrufende Methode kann sie dann wie benötigt fangen und handhaben.

0

Mit Blick auf Ihren Code möchten Sie wahrscheinlich Ausnahmen auslösen, hauptsächlich weil beide Zeichenfolgen, die Sie zurückgeben, scheinen etwas außergewöhnliches aufgetreten ist.

Fragen Sie sich auch: "Wenn diese Methode zurückkehrt, wie werde ich mit der Ausgabe umgehen?" Wenn Sie den Typ nicht kennen, können Sie sicher Dinge wie if tun (Ergebnis ist String), aber möchten Sie Ihren Code damit streuen? Ist es nicht einfacher, stattdessen eine Ausnahme zu behandeln?

Wenn Sie es wirklich so tun müssen, können Sie Aufgabe <Objekt> zurückgeben, Sie müssen nur den Typ des Ergebnisses vom Aufrufer überprüfen, wenn Sie das tun. Zum Beispiel:

var result = await GetAllReports(token, username) 
if (result is string) 
{ 
    //Do Something 
} 
if(result is List<AllReportVM>) 
{ 
    //Do Something 
} 

public async Task<object> GetAllReports(string token, string username) 
{ 
    var httpClient = GetHttpClient(); 
    if (CrossConnectivity.Current.IsConnected) { 
     var response = await httpClient.GetAsync ("getdashboardreports.ashx").ConfigureAwait (false); 

     if (response.IsSuccessStatusCode) { 
      var content = response.Content; 

      string jsonString = await content.ReadAsStringAsync().ConfigureAwait (false); 

      return JsonConvert.DeserializeObject<List<AllReportVM>> (jsonString); 
     } else { 
      return "SystemError"; 
     } 
    } else { 
     return "Internet Connectivity Error"; 

    } 
} 
1

Meine erste Wahl wäre, eine Ausnahme in den Fehlerfällen zu werfen.

public async Task<List<AllReportVM>> GetAllReports(string token, string username) 
{ 
    var httpClient = GetHttpClient(); 
    if (CrossConnectivity.Current.IsConnected) { 
     var response = await httpClient.GetAsync ("getdashboardreports.ashx").ConfigureAwait (false); 

     if (response.IsSuccessStatusCode) { 
      var content = response.Content; 

      string jsonString = await content.ReadAsStringAsync().ConfigureAwait (false); 

      return JsonConvert.DeserializeObject<List<AllReportVM>> (jsonString); 
     } else { 
      throw new Exception("SystemError"); 
     } 
    } else { 
     throw new Exception("Internet Connectivity Error"); 

    } 
} 

Es würde erfordern, dass der Aufrufer die Ausnahme abfängt, aber dann brauchen Sie nur einen einzigen Rückweg.

Meine zweite Wahl wäre, ein out Argument hinzuzufügen, um den Ausnahmegrund zu nehmen. Hinweis: Dieser Ansatz funktioniert nicht mit asynchronen Methoden.

Wenn das nicht akzeptabel ist, könnten Sie versuchen, das Tuple mit der Hälfte des Tupels als null zurückzugeben.

+0

Sie können out-Parameter in Async-Methoden verwenden. –

+0

Ich habe vergessen, dass Sie 'out' Parameter nicht für' async' Methoden verwenden können. Dann denke ich, eine Ausnahme zu werfen ist der beste Ansatz. –

Verwandte Themen