【问题标题】:How to return result with the list of errors from function如何使用函数的错误列表返回结果
【发布时间】:2011-08-13 00:05:18
【问题描述】:

我在尝试返回结合函数调用的一组错误的结果时遇到了困难。

我是从以下方法入手的

List<String> errors;
bool result = Obj.GetResult(out errors, int id);

第二步是引入新的类

public class OperationResult
{
  public bool Result {get;set;}
  public List<string> Errors {get;set;}

}

但随后脏代码开始出现在 GetResult 方法中。

例如

public OperationResult GetResult(int id)
{
  if (id == 0)
  {
    return new OperationResult { Result = false, Errors = {"Error"}};
  }

  if (id < 400)
  {
    var result = new OperationResult { Result = false, Errors = {"Error"}};
    if (id >200)
       result.Errors.Add("Error");
    return result;
  }
}

然后我开始担心不允许结果用户编辑他们得到的结果。

我已经从只能读取数据的 OpearationResult 类中提取了接口。

现在我想添加结果生成器类...

在这一点上,我开始认为我做错了什么。试图重新发明自行车,或者只是无中生有地制造问题。

请给我一个建议,如何处理所有这些东西。 我也很好奇这是如何在函数式编程语言中处理的。 (我的意思是不变性)

【问题讨论】:

  • 有什么理由不使用异常?
  • 从不喜欢过度使用异常,原因可能与迫使 MS 放弃基于异常的 ui 验证的原因相同。
  • 你只能过度使用 throw 语句。将异常作为对象返回是可以的。返回 bool 的方法是非常邪恶的。

标签: c# design-patterns function functional-programming


【解决方案1】:

我只是想对这个问题(C# 7 及更高版本)提供一个当代的看法,因为我在 DomainResult NuGet 包中遇到了类似的挑战,它有 50 多种扩展方法来返回结果和错误。

在您的情况下,OperationResult 类将具有这些属性(注意只读错误集合)

IReadOnlyCollection<string> Errors { get; } // Collection of error messages if any
bool IsSuccess { get; }                     // Flag, whether the current status is successful or not
DomainOperationStatus Status { get; }       // Current status of the domain operation: Success, Error, NotFound

从这里你有两个选择:

  1. 有一个通用的OperationResult&lt;T&gt;,它通过添加T Value { get; } 属性来扩展上面的那个。
  2. 从方法中返回ValueTuple,例如(T, OperationResult)

现在可以使用多种扩展方法为其增添趣味,例如

// Successful result with an int
(value, state) = OperationResult.Success(10);        // value = 10; state.Status is 'Success'
// The same but wrapped in a task
var res = OperationResult.SuccessTask(10);           // res is Task<(int, OperationResult)>

// Error message
OperationResultres = OperationResult.Error("Ahh!");  // res.Status is 'Error' and res.Errors = new []{ "Ahh!" }
// Error when expected an int
(value, state) = OperationResult.Error<int>("Ahh!"); // value = 0, state.Status is 'Error' and state.Errors = new []{ "Ahh!" }

https://github.com/AKlaus/DomainResult 上查看示例。都是你的。

【讨论】:

    【解决方案2】:

    这可能是开始抛出异常的时候了。 在他的书Clean Code 中,Robert Martin 谈到了当您执行以下操作时会出现混乱和缺乏可读代码:

    CustomResult result = myUser.CanLogOn();
    

    因为从阅读代码中可以看出,CanLogOn 应该简单地返回一个布尔值,指示用户是否被允许登录,但现在它得到了一个包含错误代码等的自定义结果对象。这将导致您进一步污染您的像

    这样的代码
    if(result.Result)
       LogOn();
    

    而不是

    if(myUser.CanLogOn())
       LogOn();
    

    甚至更好

    myUser.LogOn() 
    

    让它决定是否可以。

    这是一个简化的示例,因为我假设除了数据库异常之外,CanLogOn() 方法中不会发生很多可能的错误。

    虽然您不应该对正常流程使用异常,但它们的存在部分是为了防止这种输出值模式和返回错误代码,并且需要知道返回代码 200 和 402 之间的区别和-134。

    这将使您的代码更简洁、更易于阅读,并且可能会提示您检查您是否真的需要所有这些异常,如果需要,这种方法是否是最好的抛出它们的地方。

    【讨论】:

      【解决方案3】:

      我认为您需要的保护级别可以通过readonly 关键字访问:

      internal class OperationResult
      {
          public OperationResult(bool result, List<string> errors)
          {
              Result = result;
              Errors = errors;
          }
      
          readonly bool Result { get; set; }
          readonly List<string> Errors { get; set; }
      }
      

      使用此修饰符,这两个属性只能在构造函数中设置,因此您的客户端无法修改它们。因此,要随心所欲地使用它们,只需修改一下您的程序:设置错误列表,然后构建创建您的 OperationResult 对象:

      public OperationResult GetResult(int id)
      {
        if (id == 0)
        {
          return new OperationResult(false, new List<string>{"Error"});
        }
      
        if (id < 400)
        {
          var errors = new List<string>{"Error"};
          if (id >200)
             errors.Add("Error");
          return new OperationResult(false, errors);
        }
      }
      

      【讨论】:

        【解决方案4】:

        发明了例外以防止您在这里陷入混乱。

        其他不返回bool的方法呢?

        我可以想到你的方法的其他问题,但主要问题是:它需要更多的工作(与投掷相比),而且很容易犯错。报错时出错...

        【讨论】:

          猜你喜欢
          • 2018-05-04
          • 2013-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多