我在尝试返回结果与函数调用中的错误集合时遇到了困难。
我从以下方法开始
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类中提取了只能读取数据的接口。
现在我想添加结果构建器类...
此时我开始认为我做错了什么。试图重新发明自行车,或者只是在空中制造问题。
请给我一个建议,如何处理所有这些东西。 我也很好奇这是如何在函数式编程语言中处理的。 (我的意思是不变性)
答案 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个选择:
OperationResult<T>
,可以通过添加T Value { get; }
属性来扩展上述内容。(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处检查样本。都是你的。