在函数参数验证时抛出异常是否被认为是一种好的做法,就像我对TryPassNameValidation函数所做的那样,如果:
- 代码没有直接面向UI。它是API的一部分
- 至关重要的是,执行不能以错误的论点继续下去
- 我想要良好的可读性和流畅性
我也考虑过返回一个验证对象,就像这个答案中描述的那样,但这种编程风格让我想起了GoLang函数处理错误的方式。我认为发明例外是为了防止我们不得不这么做。
public void AddChildStructureLevel(Structure childStructureLevel)
{
try
{
TryPassNameValidation(childStructureLevel.Name); // throws if something is wrong
Children.Add(childStructureLevel);
}
catch (ArgumentException ae)
{
throw new ArgumentException($"Provided structure name {childStructureLevel.Name} is invalid.", ae);
}
Trace.WriteLine($"Added new child structure level to parent structure children.");
}
private void TryPassNameValidation(string structureName)
{
TryPassingStructureNameMinMaxLength(structureName);
TryPassingStructureNameUnique(structureName);
}
private void TryPassingStructureNameMinMaxLength(string structureName)
{
bool nameIsTooLong = structureName.Length >= maxLengthStructureName;
bool nameIsTooShort = structureName.Length <= minLengthStructureName;
if (nameIsTooLong) throw new ArgumentException($"Structure name is too long. Max {maxLengthStructureName} characters.");
if (nameIsTooShort) throw new ArgumentException($"Structure name is too short. Min {minLengthStructureName} characters.");
}
private void TryPassingStructureNameUnique(string childName)
{
foreach (Structure child in Children) //TODO: recurse over children.children
if (child.Name == childName) throw new ArgumentException($"Structure level name {childName} already exists.");
}
我发现这段代码很难理解,这与您的目标背道而驰。验证是通过嵌套调用和不明显的控制流(异常)来完成的。我发现每种方法开头的常见if ... throw;
序列更容易理解。部分原因是几乎所有的验证代码都是这样的。
应该使用Any
来编写TryPassingStructureNameUnique
。之后,它适合一行,可以用于上述if ... throw;
验证模式。
bool nameIsTooLong
的内容非常冗长。这些条件微不足道。他们应该只使用if ... throw;
模式。
此外,我认为这个设计是一个有效的选择。这不是一个容易的选择。如果我能帮忙的话,我不会那样做的
如果有效性是这样一个核心概念,那么考虑将每个Structure
包装在ValidatedStructure
包装类中。施工时进行验证。这样,您就可以从视觉检查中知道给定的Structure
是否已经验证。
这取决于API的含义。
如果你指的是一个业务逻辑,你可以预测一个无效的输入,比如空用户名,那么我不会抛出任何异常。
然而,如果你指的是一个框架(团队共享的库),因为API无法使用无效参数,所以你想向用户指示其无效使用,那么我会抛出一个异常。