如何使用函数中的错误列表返回结果

时间:2011-04-26 12:12:15

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

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

我从以下方法开始

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类中提取了只能读取数据的接口。

现在我想添加结果构建器类...

此时我开始认为我做错了什么。试图重新发明自行车,或者只是在空中制造问题。

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

4 个答案:

答案 0 :(得分:3)

这可能是开始抛出异常的时候。 罗伯特·马丁在他的书Clean Code中谈到了当你做类似的事情时混淆和缺乏可读代码:

CustomResult result = myUser.CanLogOn();

因为从阅读代码中可以看出,CanLogOn应该只返回一个bool,指示是否允许用户登录,但现在它正在获取一个包含错误代码等的自定义结果对象。这将导致您进一步污染您的用

之类的代码
if(result.Result)
   LogOn();

而不是

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

甚至更好

myUser.LogOn() 

并让它决定是否可以。

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

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

它会使您的代码更清晰,更易于阅读,并且可能会提示您检查是否确实需要所有这些异常,如果是,则此方法是将它们抛出的最佳位置。

答案 1 :(得分:1)

发明例外是为了防止你进入这里的混乱局面。

其他不返回bool的方法呢?

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

答案 2 :(得分:1)

我认为您需要的保护级别可以通过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; }
}

使用此修饰符,只能在构造函数中设置2个属性,因此客户端无法修改它们。因此,要根据需要使用它们,只需修改一下程序:设置错误列表,然后构建创建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);
  }
}

答案 3 :(得分:0)

我只是想对这个问题(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

从这里开始,您有2个选择:

  1. 具有一个通用的OperationResult<T>,可以通过添加T Value { get; }属性来扩展上述内容。
  2. (T, OperationResult)之类的方法中返回ValueTuple

现在通过多种扩展方法(例如

)为其增添趣味
// 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处检查样本。都是你的。