从方法返回多个结果

时间:2012-01-19 17:15:43

标签: c# return

我尝试使用Try Catch块提高我的技能并更好地处理错误。

我有一个执行常见任务的类,在这种情况下检索Facebook AccessToken。如果成功,我想返回AccessToken字符串,如果不是,我想返回错误消息。这些都是字符串,所以没问题。但是当检查代码调用端的返回值时,如何有效地执行此操作?

这就像我需要返回2个值。在成功尝试的情况下,返回= true,“ACESSCODEACXDJGKEIDJ”,或者如果失败,则返回= false,“Ooops,出现错误”+ ex.ToString();

然后检查返回值很容易(理论上)。我可以想到返回一个true / false返回,然后为字符串设置Session变量。

从方法返回多个结果的方法是什么?

11 个答案:

答案 0 :(得分:22)

创建一个Result类并返回它......

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 
}

答案 1 :(得分:8)

有两种可能性让人想起

  1. 使用TryXXX模式(在某些BCL方法中使用,例如DateTime.TryParse)。
  2. 设计一个包含操作状态和结果的类,然后让您的方法返回此类。
  3. 让我们首先看一下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
    }
    

    当然,结果可以是任何其他复杂对象,而不是(如本例所示)字符串。

答案 2 :(得分:7)

基于musefan的答案,我喜欢相同的模式,但是使用通用的Result类型,所以我可以在整个代码库中使用它:

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

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

我喜欢这个与抛出一个异常返回数据的函数的一个原因是,这有助于您将该函数映射到集合上,捕获错误消息中的异常详细信息,因此您不必担心炸毁整个链条的一个项目的例外情况。这对于从平面数据文件中解析行的情况很有用,成功的行应向前移动,但任何错误都应单独处理:

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#有多个返回值的日子。

答案 3 :(得分:5)

尝试一个元组?

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

答案 4 :(得分:3)

一种很好的方法是返回一个包含成功/失败状态和详细错误消息的对象。

类似的东西:

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

答案 5 :(得分:2)

  

如果成功,我想返回AccessToken字符串,如果不是,我想返回错误消息。这些都是字符串,所以没问题。但是当检查代码调用端的返回值时,如何有效地执行此操作?

C#并不真正使用错误消息,我们使用exceptions。执行此操作的正确方法是抛出异常,让调用者忽略或捕获它。

如果失败不是“例外”(例如,如果某些用户有令牌而有些用户没有),那么另一种方法是返回一个空字符串来表示没有令牌(并且仍然会抛出异常)对于“特殊”案件,例如无法联系Facebook等)。我不认为你是这样的,因为你的示例失败包括一个Exception对象。

最重要的是,您通常会将异常处理(catch)留给堆栈的最顶层(通常是UI),因为它具有当前操作的最大上下文。捕获异常,重新格式化为字符串,然后将其返回 - 在此过程中丢失有价值的异常信息是没有用的。只是让调用者有异常,他们可以决定如何向用户呈现该失败(或者在没有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();
      }
   }
}

答案 6 :(得分: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

通过这种方法,可以传递catch块中的ex.Message和try块中的Data<T>

答案 7 :(得分:1)

我不会返回错误消息。返回一个有意义的值或错误,让它冒出来。您如何处理错误取决于您,但至少我会在前端优雅地处理它并在后端记录/通知某人。

如果你坚持返回一些东西,即使你的函数出错了,那么我会返回一个有以下成员的对象:

Value - String
Success - Bool

然后您可以检查是否成功并相应地处理该值。

答案 8 :(得分: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
}

这里要注意的重要一点是,当您只关心操作是否有效时,通常会使用此方法(并且您并不真正关心为什么如果失败则无法正常工作并且合理地期望它可能不会。

如果失败是例外(意味着正确配置的程序不应该遇到此错误),那么您应该使用例外。事实上,如果你的函数实际上不能任何事情而你得到的例外,那么实际捕获它是没有意义的。正确的异常处理意味着让异常“冒泡”到程序中的任何层实际上可以做一些有意义且适当的异常。

这也简化了您的场景,因为您只需要返回一个字符串。

答案 9 :(得分:0)

为什么不创建具有3个属性的类。成功(bool),消息(字符串)和令牌(字符串)。您可以创建该类的实例,填充值并返回该值。

答案 10 :(得分:0)

如果要返回2个对象,可以执行以下操作:

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

        errorMessage = "";
        return true;
    }

然后你得到bool和错误信息