如何从函数返回带有错误列表的结果

本文关键字:有错误 列表 结果 返回 函数 | 更新日期: 2023-09-27 17:53:14

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

我从以下方法开始

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

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

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

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

如何从函数返回带有错误列表的结果

这可能是开始引发异常的时候了。在他的《清洁代码》一书中,罗伯特·马丁谈到了当你做这样的事情时,混乱和缺乏可读的代码:

CustomResult result = myUser.CanLogOn();

因为从读取代码来看,CanLogOn 应该只返回一个布尔值,指示是否允许用户登录,但现在它正在获取一个具有错误代码等的自定义结果对象。 这将导致你进一步污染你的代码,比如

if(result.Result)
   LogOn();

而不是

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

甚至更好

myUser.LogOn() 

并让它决定它是否可以。

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

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

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

发明例外是为了防止你在这里陷入混乱。

其他不返回bool的方法呢?

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

我认为您可以通过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);
  }
}

只是想提供对该问题(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)

现在用多种扩展方法为它增添趣味,例如

// 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 查看示例。这一切都是你的。