2012-01-19 13 views
12

Estoy tratando de mejorar mis habilidades usando los bloques Try Catch y un mejor manejo de errores.Devolución de resultados múltiples desde un método

Tengo una clase que realiza una tarea común, en este caso, recuperar un Facebook AccessToken. Si tiene éxito, quiero devolver la cadena AccessToken, si no, quiero devolver un mensaje de error. Estas son ambas cadenas, así que no hay problema. Pero al verificar el valor de retorno en el lado de la llamada del código, ¿cómo se puede hacer esto de manera efectiva?

Es como si tuviera que devolver 2 valores. En el caso de un intento exitoso, return = true, "ACESSCODEACXDJGKEIDJ", o si falla, return = false, "Ooops, hubo un error" + ex.ToString();

Luego, verificar el valor de retorno es fácil (en teoría). Podría pensar en devolver simplemente un verdadero/falso para el retorno y luego establecer una variable de sesión para las cadenas.

¿Qué es una forma de devolver resultados múltiples desde un método?

Respuesta

21

crear una clase y la devuelve que en vez ...

public class Result 
{ 
    public bool Success {get;set;} 
    public string AccessToken {get;set;} 
    public string ErrorMessage {get;set;} 
} 


public Result GetFacebookToken() 
{ 
    Result result = new Result(); 

    try{ 
     result.AccessToken = "FACEBOOK TOKEN"; 
     result.Success = true; 
    } 
    catch(Exception ex){ 
     result.ErrorMessage = ex.Message; 
     result.Success = false; 
    } 

    return result; 
} 

A continuación, puede llamar a este código como ...

Result result = GetFacebookToken(); 

if(result.Success) 
{ 
    //do something with result.AccessToken 
} 
else 
{ 
    //do something with result.ErrorMessage 
} 
+0

También puede hacerlo genérico sobre cualquier tipo de resultado, no ju st 'AccessToken' – Alexander

1

No devolvería el mensaje de error. Devuelve un valor o error significativo y déjalo burbujear. La forma en que maneje el error depende de usted, pero como mínimo lo manejaría elegantemente en el frente y registraría/notificaría a alguien en el back-end.

Si insiste en devolver algo aún cuando sus errores de función a cabo entonces volverían un objeto que tiene los siguientes miembros:

Value - String 
Success - Bool 

entonces usted puede comprobar para manejar el éxito y el valor en consecuencia.

3

Una forma bonita de hacerlo es devolver un objeto que incluya tanto un estado Correcto/Fallo como un mensaje de error detallado.

algo como:

class Result 
{ 
    bool IsSuccessful { get; set; } 
    string DetailedStatus { get; set; } 
} 
8

2 posibilidades vienen a la mente

  1. utilizar el patrón TryXXX (utilizado en algunos métodos BCL tales como DateTime.TryParse).
  2. Diseña una clase que contenga el estado de la operación y el resultado y luego haz que tu método devuelva esta clase.

Primero veamos el patrón TryXXX. Básicamente es un método que devuelve un valor booleano y el resultado como parámetro out.

public bool TryXXX(string someInput, out string someResult, out string errorMessage) 
{ 
    ... 
} 

que se consume de esta manera:

string someResult; 
string errorMessage; 
if (!TryXXX("some parameter", out someResult, out errorMessage)) 
{ 
    // an error occurred => use errorMessage to get more details 
} 
else 
{ 
    // everything went fine => use the results here 
} 

En el segundo enfoque sólo tendría que diseñar una clase que contendrá toda la información necesaria:

public class MyResult 
{ 
    public bool Success { get; set; } 
    public string ErrorMessage { get; set; } 

    public string SomeResult { get; set; } 
} 

y luego tener su método devuelve esta clase:

public MyResult MyMethod(string someParameter) 
{ 
    ... 
} 

que se consume como esto:

MyResult result = MyMethod("someParameter"); 
if (!result.Success) 
{ 
    // an error occurred => use result.ErrorMessage to get more details 
} 
else 
{ 
    // everything went fine => use the result.SomeResult here 
} 

Por supuesto, los resultados pueden ser cualquier otro objeto complejo en lugar de (tal como se muestra en este ejemplo) una cadena.

0

¿Por qué no creas una clase con 3 propiedades. success (bool), message (string) y token (string). Puede crear una instancia de esa clase, rellenar los valores y devolverlos.

0

Si desea volver 2 objetos, puede hacer algo como esto:

private bool TestThing(out string errorMessage) 
    { 
     bool error = true; 
     if(error) 
     { 
      errorMessage = "This is a message!"; 
      return false; 
     } 

     errorMessage = ""; 
     return true; 
    } 

continuación presentamos lo mejor bool y el mensaje de error

1

Definitivamente es correcto que el uso de una ubicación de almacenamiento externa (una variable de sesión, por ejemplo) es el forma incorrecta.

El enfoque correcto depende de si considera o no que un error es circunstancia excepcional. Si no es así, a continuación, seguir el ejemplo dado en el marco anteponiendo su función con la palabra Try y que tiene su firma este aspecto:

public bool TryGetFacebookToken(<necessary parameters>, out string token) 
{ 
    ... set the token within the body and return true if it succeeded or false if it did not 
} 

Lo importante a destacar aquí es que este enfoque se utiliza generalmente cuando sólo cuide si la operación funcionó o no (y no le importa realmente por qué no funcionó si falló) y tiene una expectativa razonable de que no funcione.

Si un fallo es excepcional (lo que significa que un programa correctamente configurado no debe encuentra este error), entonces se debe utilizar una excepción. De hecho, si su función no puede realmente hacer cualquier cosa con la excepción que está recibiendo, entonces no tiene sentido capturarla. El manejo adecuado de las excepciones significa dejar que la excepción "burbujee" a cualquier capa en su programa que realmente pueda hacer algo significativo y apropiado con la excepción.

Esto también simplifica su escenario, ya que solo necesita devolver una cadena.

2

Si tiene éxito, quiero devolver la cadena AccessToken, si no, quiero devolver un mensaje de error. Estas son ambas cadenas, así que no hay problema. Pero al verificar el valor de retorno en el lado de la llamada del código, ¿cómo se puede hacer esto de manera efectiva?

C# realmente no utiliza mensajes de error, usamos exceptions. La forma correcta de hacerlo es lanzar una excepción y dejar que la persona que llama ignore o atrape.

Si no es "excepcional" fallar (p. Ej., Si algunos usuarios tienen tokens y otros no), una alternativa sería devolver una cadena nula para indicar la ausencia de un token (y aún lanzar un excepción para casos "excepcionales" como no poder contactar a Facebook, etc.). No creo que ese sea el caso para usted, ya que su falla de ejemplo incluyó un objeto Exception.

La línea inferior, es que generalmente deja el manejo de excepciones (catch) en la parte superior de la pila (generalmente, la IU) ya que tiene el mayor contexto de la operación actual. No sirve de nada capturar una excepción, volver a formatear a una cadena y luego devolverla, perdiendo valiosa información de excepción en el camino. Simplemente deje que quien llama tenga la excepción en su lugar, y ellos pueden decidir cómo presentar esa falla al usuario (o continuar sin integración de FB).

Esto es, obviamente, burlado, pero esperemos que se pone mi punto de vista (código habla más que mil palabras):

class Facebook { 
    ... 
    public string GetAccessToken(string username, string password) { 
     // can throw WebException if can't connect to FB 
     this.Connect(); 

     // returns null token if not a Facebook user 
     if (!this.IsUser(username)) return null; 

     // can throw ArgumentException if password is wrong 
     var fbInfo = this.GetInfo(username, password); 

     return fbInfo.AccessToken; 
    } 
    ... 
} 

class Page { 
    void Page_Load(object sender, EventArgs e) { 
     var fb = new Facebook(); 

     string accessToken; 
     try { 
     accessToken = fb.GetAccessToken(this.User.Name, this.txtPassword.Text); 
     } catch (WebException ex) { 
     Log(ex); 
     this.divError.Text = "Sorry, Facebook is down"; 
     // continue processing without Facebook 
     } catch (ArgumentException ex) { 
     // Don't log - we don't care 
     this.divError.Text = "Your password is invalid"; 
     // stop processing, let the user correct password 
     return; 
     } catch (Exception ex) { 
     Log(ex); 
     // Unknown error. Stop processing and show friendly message 
     throw; 
     } 

     if (!string.IsNullOrEmpty(accessToken)) { 
     // enable Facebook integration 
     this.FillFacebookWallPosts(accessToken); 
     } else { 
     // disable Facebook integration 
     this.HideFacebook(); 
     } 
    } 
} 
4

Pruebe un par de valores?

public Tuple<bool, string> ReturnsBoolAndString() { 
    return Tuple.Create(false, "string"); 
} 
6

Para construir sobre la respuesta de musefan, me gusta el mismo patrón pero con un tipo de resultado genérico para que pueda utilizarlo durante todo el código base:

public class Result 
{ 
    public bool Success { get; set; } 
    public string ErrorMessage { get; set; } 
} 

public class Result<T> : Result 
{ 
    public T Data; 
} 

Una de las razones que me gusta este vs lanzar una excepción de una función que devuelve datos, es que esto le ayuda a mapear esa función sobre una colección, capturando detalles de excepción en mensajes de error, para que no tenga que preocuparse por una excepción en un elemento que explota toda la cadena. Esto es bueno para situaciones como analizar las líneas de un archivo de datos plana, donde las líneas exitosas deben moverse hacia adelante, pero los errores deben ser tratados de forma individual:

public static Result<Thing> ParseThing(string line) 
{ 
    try 
    { 
      // Parse a Thing (or return a parsing error.) 
      return new Result<Thing> { Data = thing, Success = true }; 
    } 
    catch (Exception ex) 
    { 
      return new Result<Thing> { Data = null, Success = false, ErrorMessage = "..." }; 
    } 
} 

... 

var results = lines.Select(ParseThing); 

foreach (var result in results) 
{ 
    // Check result.Success and deal with successes/failures here. 
} 

Por supuesto, usted todavía tiene la opción de lanzar una excepción a cabo de esa función para situaciones realmente excepcionales cuando explota toda la cadena de procesamiento es lo que desea.

P.S. Todos los días es el día en que deseo que C# tenga múltiples valores de retorno.

+0

Actualmente tiene múltiples valores de retorno ahora.Puede usar Tuple o Tuple , etc. Consulte https://msdn.microsoft.com/en-us/library/dd268536(v=vs.110).aspx – stefann

+0

Bien, valores devueltos múltiples directamente como construcciones de lenguaje, en el espíritu de Lua o Go. (Perdón, no mover los postes del objetivo.) Algo como: asunto público, cadena ParseThing (...) {...} var thing, err = ParseThing (...); No lo he pensado bien; Estoy seguro de que hay buenas razones por las cuales no encajaría con el idioma. – user1454265

+0

Sé lo que quiere decir y está de acuerdo. Podría ser azúcar sintáctico en la parte superior de la construcción Tuple. Debe presentar una sugerencia a través de Visual Studio. – stefann

2

Una aplicación más genérica será

C#

public class ReturnMessage<T> 
{ 
    //indicates success or failure of the function 
    public bool IsSuccess { get; set; } 
    //messages(if any) 
    public string Message { get; set; } 
    //data (if any) 
    public T Data { get; set; } 
} 

VB.NET

Public Class ReturnMessage(Of T) 
    'indicates success or failure of the function 
    Public Property IsSuccess As Boolean 
    'messages(if any) 
    Public Property Message As String 
    'data (if any) 
    Public Property Data As T 
End Class 

Mediante este método se puede pasar la ex.Message en el bloque catch y Data<T> en el bloque try

Cuestiones relacionadas