2012-01-19 4 views
12

Try Catchブロックを使用してスキルを向上させ、エラー処理を改善しようとしています。メソッドから複数の結果を返す

共通のタスクを実行するクラスがあります。この場合、FacebookのAccessTokenを取得しています。成功した場合は、AccessToken文字列を返したい場合は、エラーメッセージを返す必要があります。これらは両方とも文字列なので問題ありません。しかし、コードの呼び出し側で戻り値をチェックするとき、どのようにこれを効果的に行うことができますか?

2つの値を返す必要があります。試行が成功した場合は、return = true、 "ACESSCODEACXDJGKEIDJ"、失敗した場合は、戻り値= false、 "Ooops、エラーが発生しました" + ex.ToString();

戻り値を確認するのは簡単です(理論上)。返り値のtrue/falseを返すだけで、文字列のSession変数を設定することができます。

メソッドから複数の結果を返す方法はありますか?

答えて

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. は(例えばDateTime.TryParseなどのいくつかのBCL方法において使用される)TryXXXパターンを使用する気に春。
  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つのプロパティを持つクラスを作成しないでください。成功(ブール)、メッセージ(文字列)、トークン(文字列)。そのクラスのインスタンスを作成し、値を移入して戻すことができます。あなたは2つのオブジェクトを返すようにしたい場合は

0

は、あなたがこのような何かを行うことができます。

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オブジェクトが含まれているので、そうは思わないでしょう。

ボトムラインは、現在の操作のコンテキストが最も多いスタック(通常はUI)の最上部に例外処理(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; 
} 

一つの理由それ以外の方法でデータを返す関数の例外は、エラーメッセージの例外の詳細をキャプチャしてコレクション全体にその関数をマップするのに役立ちます。したがって、チェーン全体を爆発させる1つの項目の例外について心配する必要はありません。これが成功したラインが前方に移動する必要がありますが、何らかのエラーが個別に対処しなければならないフラットなデータファイル、外のラインを解析するような状況のために良いされています。もちろん

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

より一般的な実装では、1つのtryブロックにcatchブロックとData<T>ex.Messageを渡すことができますこの方法により

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 

になります