2012-01-19 2 views
12

Я пытаюсь улучшить свои навыки, используя блоки Try Catch и лучшую обработку ошибок.Возвращение нескольких результатов по методу

У меня есть класс, который выполняет общую задачу, в этом случае извлекает Facebook AccessToken. В случае успеха я хочу вернуть строку AccessToken, если я не хочу возвращать сообщение об ошибке. Это обе строки, поэтому проблем нет. Но когда вы проверяете возвращаемое значение на вызывающей стороне кода, как вы можете это сделать эффективно?

Как будто мне нужно вернуть 2 значения. В случае успешной попытки return = true, «ACESSCODEACXDJGKEIDJ», или если он не работает, return = false, «Ooops, была ошибка» + ex.ToString();

Тогда проверка возвращаемого значения легко (теоретически). Я мог подумать о возвращении просто истинного/ложного для возврата, а затем установки переменной сеанса для строк.

Что такое способ возврата нескольких результатов из метода?

ответ

21

Создать класс Результат и вернуться, что вместо того, чтобы ...

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; 
} 

Тогда вы можете вызвать этот код, как ...

Result result = GetFacebookToken(); 

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

Может также сделать его общим по любому типу результата, а не ju st 'AccessToken' – Alexander

1

Я не верну сообщение об ошибке. Верните значащее значение или ошибку и дайте ему пузыриться. Как вы справляетесь с ошибкой, зависит от вас, но, как минимум, я бы изящно обрабатывал ее на переднем конце и регистрировал/уведомлял кого-то на сервере.

Если вы настаиваете на возвращении кое-что, даже если ваши ошибки функция из то я бы возвращать объект, который имеет следующие элементы:

Value - String 
Success - Bool 

Затем вы можете проверить успех и обрабатывать значения соответственно.

3

Приятный способ сделать это - вернуть объект, который включает в себя как статус «Успех/Сбой», так и подробное сообщение об ошибке.

что-то вроде:

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

2 возможностей весной на ум

  1. Используйте шаблон TryXXX (используемый в некоторых BCL методов, таких как DateTime.TryParse).
  2. Создайте класс, содержащий статус операции и результат, а затем возвращайте этот метод.

Давайте сначала рассмотрим шаблон TryXXX. Это в основном метод, который возвращает логическое значение, а результат - как параметр out.

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

, который будет потребляться, как это:

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 
} 

Во втором подходе вы бы просто создать класс, который будет содержать всю необходимую информацию:

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

    public string SomeResult { get; set; } 
} 

и тогда ваш метод возвратить этот класс:

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

, который будет потребляться как это:

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 
} 

Конечно, результаты могут быть любой другой сложный объект вместо (как показано в этом примере) строку.

0

Почему бы вам не создать класс с 3 свойствами. успех (bool), сообщение (строка) и токен (строка). Вы можете создать экземпляр этого класса, заполнить значения и вернуть их.

0

Если вы хотите вернуть 2 объекта, вы можете сделать что-то вроде этого:

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

     errorMessage = ""; 
     return true; 
    } 

тогда вы получите логическое значение и сообщение об ошибке

1

Вы, безусловно, правы, что использование внешнего хранилища (например, переменной сеанса) является неправильным способом.

Правильный вариант зависит от того, считается ли вы ошибкой как исключительное значение. Если нет, то следовать примеру в рамках предваряя ваши функции со словом Try и иметь свою подпись выглядеть следующим образом:

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 
} 

Важно отметить, что этот подход обычно используется, когда вы только забота о том, работала ли операция (и вам все равно , почему это не сработало, если она не удалась), и у вас есть разумное ожидание, что она не может.

Если отказ исключительного (это означает, что правильно настроена программа не должны столкнуться с этой ошибкой), то вы должны использовать исключение. На самом деле, если ваша функция не может на самом деле сделать ничего, за исключением того, что вы получаете, тогда нет смысла на самом деле ее поймать. Правильная обработка исключений означает, что исключение «пузырится» на любой уровень вашей программы может действительно сделать что-то значимое и соответствующее исключению.

Это также упрощает ваш сценарий, поскольку вам нужно вернуть строку.

2

В случае успеха, я хочу вернуть строку AccessToken, если нет, я хочу вернуть сообщение об ошибке. Это обе строки, поэтому проблем нет. Но когда вы проверяете возвращаемое значение на вызывающей стороне кода, как вы можете это сделать эффективно?

C# на самом деле не использует сообщения об ошибках, мы используем exceptions. Правильный способ сделать это - просто выбросить исключение и позволить вызывающему абоненту игнорировать или улавливать его.

Если это не «исключительный» сбой (например, если у некоторых пользователей есть токены, а у некоторых нет), то альтернативой было бы вернуть пустую строку, указывающую на отсутствие токена (и все еще бросать исключение для «исключительных» случаев, таких как невозможность связаться с Facebook и т. д.). Я не думаю, что это так, потому что ваш пример неудачи включал объект Exception.

Подводящий итог, заключается в том, что вы обычно оставляете обработку исключений (catch) до самой верхней части стека (обычно, пользовательский интерфейс), поскольку это имеет самый контекст текущей операции. Не имеет смысла ловить исключение, переформатировать строку и затем вернуть это вместо этого - потерять важную информацию об исключении на этом пути. Просто позвольте вызывающему абоненту иметь исключение вместо этого, и они могут решить, как представить эту ошибку пользователю (или продолжить без интеграции FB).

Это, очевидно, издевались, но, надеюсь, получает мой пункт через (код говорит громче, чем слова):

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

Попробуйте кортеж?

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

Чтобы построить на ответ musefan, я люблю по той же схеме, но с общим типом результата, так что я могу использовать его на протяжении всей кодового:

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

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

Одна из причин, мне нравится этот vs. выбрасывание исключение из функции, которая в противном случае возвращает данные, заключается в том, что это помогает вам сопоставить эту функцию над коллекцией, фиксируя данные о деталях в сообщениях об ошибках, поэтому вам не нужно беспокоиться об исключении на одном элементе, взорвавшем всю цепочку. Это хорошо для ситуаций, как разбор строки из плоского файла данных, где успешные линии должны двигаться вперед, но любые ошибки должны быть рассмотрены в индивидуальном порядке:

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. 
} 

Конечно, у вас еще есть возможность бросить исключение из этой функции для действительно исключительных ситуаций, когда взорвать всю цепочку обработки - это то, что вы хотите.

P.S. Каждый день - это день, когда я хочу, чтобы у C# было несколько возвращаемых значений.

+0

На самом деле теперь он имеет несколько возвращаемых значений.Вы можете использовать Tuple или Tuple и т. Д. См. Https://msdn.microsoft.com/en-us/library/dd268536(v=vs.110).aspx – stefann

+0

Ну, несколько возвращаемых значений непосредственно в качестве языковой конструкции, в духе Луа или Го. (Извините, чтобы не перемещать стойки ворот.) Кое-что вроде: public Thing, string ParseThing (...) {...} var thing, err = ParseThing (...); Я действительно не продумал это; Я уверен, что есть веские причины, почему это не соответствует языку. – user1454265

+0

Я знаю, что вы имеете в виду и согласитесь. Это может быть синтаксический сахар поверх конструкции Tuple. Вы должны подать предложение через Visual Studio. – stefann

2

Более общая реализация будет

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 

С помощью этого метода можно передать ex.Message в блоке улова и Data<T> в блоке Ьгу

Смежные вопросы