从方法返回多个结果[重复]

Posted

技术标签:

【中文标题】从方法返回多个结果[重复]【英文标题】:Returning multiple results from a method [duplicate] 【发布时间】:2012-02-14 08:45:06 【问题描述】:

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

我有一个执行常见任务的类,在本例中检索 Facebook AccessToken。如果成功,我想返回 AccessToken 字符串,如果不成功,我想返回错误消息。这两个都是字符串,所以没问题。但是在代码调用端检查返回值时,如何有效地做到这一点?

这就像我需要返回 2 个值。在尝试成功的情况下,return = true, "ACESSCODEACXDJGKEIDJ",或者如果失败,return = false, "Ooops, there was an error" + ex.ToString();

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

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

【问题讨论】:

【参考方案1】:

一个更通用的实现将是

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&lt;T&gt;

【讨论】:

【参考方案2】:

创建一个 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 

【讨论】:

也可以使其通用于任何结果类型,而不仅仅是AccessToken【参考方案3】:

为了基于 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.

当然,当您想要炸毁整个处理链时,您仍然可以选择在真正异常的情况下从该函数中抛出异常。

附:每天都是我希望 C# 有多个返回值的日子。

【讨论】:

其实它现在确实有多个返回值。您可以使用 Tuple 或 Tuple 等。见msdn.microsoft.com/en-us/library/dd268536(v=vs.110).aspx 嗯,多个返回值直接作为一种语言结构,本着 Lua 或 Go 的精神。 (抱歉,不要移动球门柱。)类似:public Thing, string ParseThing(...) ... var thing, err = ParseThing(...);不过,我还没有真正考虑清楚。我敢肯定它不适合这种语言是有充分理由的。 我知道你的意思并同意。它可能是 Tuple 构造之上的语法糖。您应该通过 Visual Studio 提出建议。【参考方案4】:

尝试一个元组?

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

【讨论】:

【参考方案5】:

如果成功,我想返回 AccessToken 字符串,如果不成功,我想返回错误消息。这两个都是字符串,所以没问题。但是在代码的调用端检查返回值时,如何才能有效地做到这一点呢?

C# 并不真正使用错误消息,我们使用exceptions。正确的做法是抛出一个异常,让调用者忽略或捕获它。

如果失败不是“异常”(例如,如果一些用户有令牌,而另一些用户没有),那么另一种方法是返回一个空字符串来指示不存在令牌(并且仍然抛出异常对于“特殊”情况,例如无法联系 Facebook 等)。我认为您的情况并非如此,因为您的示例失败包含一个异常对象。

底线是您通常将异常处理(捕获)留给堆栈的最顶部(通常是 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种可能性

    使用 TryXXX 模式(用于某些 BCL 方法,例如 DateTime.TryParse)。 设计一个包含操作状态和结果的类,然后让您的方法返回该类。

让我们先看看 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

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

【讨论】:

【参考方案7】:

您绝对正确,使用外部存储位置(例如会话变量)是错误方式。

正确的方法取决于您是否将错误视为异常情况。如果不是,则按照框架中设置的示例,在您的函数前面加上单词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

这里要注意的重要一点是,这种方法通常在您只关心操作是否有效时使用(而您并不真正关心 为什么如果它失败了它就不起作用) 并且有合理的预期可能不会。

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

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

【讨论】:

【参考方案8】:

如果你想返回 2 个对象,你可以这样做:

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

        errorMessage = "";
        return true;
    

然后你会得到布尔值和错误消息

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

类似:

class Result

   bool IsSuccessful  get; set; 
   string DetailedStatus  get; set; 

【讨论】:

【参考方案11】:

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

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

Value - String
Success - Bool

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

【讨论】:

以上是关于从方法返回多个结果[重复]的主要内容,如果未能解决你的问题,请参考以下文章

从c#中的方法返回多个值[重复]

具有多个结果集的NHibernate存储过程[重复]

从 MySQL 中具有不同列的表的多个连接结果中删除重复项

UNION ALL 正在返回多个结果 [重复]

Java JDBC 从不同的调用返回重复的结果集

从函数调用分配多个返回值的性能[重复]