如何从函数返回带有错误列表的结果
本文关键字:有错误 列表 结果 返回 函数 | 更新日期: 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个选择:
- 有一个通用
OperationResult<T>
,通过添加T Value { get; }
属性来扩展上面的。 - 从方法返回值元组,如
(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 查看示例。这一切都是你的。