【问题标题】:Is throwing exceptions on argument validation in an API considered a good practice?在 API 中对参数验证抛出异常是否被认为是一种好习惯?
【发布时间】:2016-04-03 20:46:05
【问题描述】:

如果:

  1. 代码没有直接面向 UI。它是 API 的一部分。
  2. 重要的是不要继续执行错误的参数。
  3. 我想要良好的可读性和流畅性。

我也考虑过返回一个验证对象,就像answer 中描述的那样,但是这种编程风格让我想起了GoLang functions handle errors 的方式。我认为例外是为了防止我们不得不这样做而发明的。

    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.");
    }

【问题讨论】:

  • 我认为codereview.stackexchange.com 是更好的提问地点。
  • 是的,尽管最终一切都还是基于意见的。但是,您的用例描述了大多数开发人员将使用异常的确切条件。
  • 感谢 Valentin 的提示。甚至不知道它的存在。

标签: c# function exception


【解决方案1】:

我发现这段代码很难理解,这与您的目标背道而驰。验证是通过嵌套调用和非显而易见的控制流(异常)来完成的。我发现每个方法开头的常用if ... throw; 序列更容易理解。部分原因是几乎所有的验证代码都是这样的。

TryPassingStructureNameUnique 应使用Any 编写。完成后,它适合一行,并且可以在提到的if ... throw; 验证模式中使用。

bool nameIsTooLong 真的很冗长。这些条件是微不足道的。他们应该只使用if ... throw; 模式。

除此之外,我认为这种设计是一个有效的选择。这不是一个容易的选择。如果我能帮上忙,我不会那样做。

如果有效性是您的核心概念,请考虑将每个 Structure 包装在 ValidatedStructure 包装器类中。验证施工。这样您就可以通过目视检查知道给定的 Structure 是否已经过验证。

【讨论】:

  • 我不对构造进行验证,因为新结构必须针对正在添加的结构进行验证。所以我必须在添加时验证它。关于嵌套调用 - 我试图让一个函数做一件事。同意其余的。
  • 这个“每个函数一个东西”的规则不是很好,因为它是什么东西是未定义的。计算structureName.Length &gt;= maxLengthStructureName 不是一回事吗?!所以分开吧! (不严重。)一件事的规则完全是假的。对于一些实际问题,例如可理解性和干净的界面,它是一个糟糕的代理。
  • 源代码通常比名称更能描述例程,同意。通过 maxNameLength 检查,我需要将功能分开。在遍历所有子结构之前,我还使用该函数验证 DeleteChildWithName 的参数。
【解决方案2】:

这取决于 API 的含义。

如果您指的是可以预测无效输入(如空用户名)的业务逻辑,那么我不会抛出任何异常。

但是,如果您指的是一个框架(一个跨团队共享的库),您希望向用户指示其无效使用,因为 API 无法使用无效参数,那么我会抛出异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-22
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多