异常处理——这是好的做法吗?

Exception Handling - is this good practice?

我想知道我要做的是好事还是坏事。我有 class:

public class Element : IElement
{
    public float? Max { get; private set; }

    public float? Min { get; private set; }

    public float? Average { get; private set; }

    public bool HasValue { get; private set; }


    public void SetRange(float? min, float? max)
    {
        if (min >= max)
        {
           throw new WrongElementValueException("Min must be greater than max!");
        }
        else if (min < 0f || max < 0f)
        {
           throw new WrongElementValueException("Min/max must be greater than 0!");
        }
        else if (min > 100f || max > 100f)
        {
           throw new WrongElementValueException("Min/max must be lesser than 0!");
        }
        else
        {
            Min = min;
            Max = max;
            Average = (min + max)/2f;
            HasValue = true;
        }
    }
}

用户将使用 SetRange() 方法设置值。但他有一些限制,比如 Min 必须大于 Max,并且两者都不能大于 100 或小于 0。

我应该在这个地方使用那些例外吗?还是有更好的方法来处理错误的用户输入? 我希望我的问题不是笼统的。

只要无法实现有效的程序流(例如与数据库的连接丢失),异常就会很有用。因此,验证用户输入并在值无效时抛出异常是绝对可以的,而您也可以使用现成的建议 ArgumentException

这是一种适当的做法,是的。

虽然我想消费代码会期待 ArgumentException 而不是这种异常类型,但这可能是一个相对次要的问题。

当输入无效,并且对象无法以其他方式有意义地继续时,抛出异常是一种适当的预期响应。正确使用对象、处理错误、向用户报告等都取决于使用代码。

此对象的替代方案是 "try to figure out what to do",这通常会导致一些非常糟糕的编码做法。例如...

  • 假设您想要 return 错误消息而不是抛出异常。如果使用代码不检查该错误消息怎么办?该对象将处于未知和无效状态,并可能悄悄地引入错误。而如果使用代码不检查异常,那么程序显然会失败,并且需要进行适当的更正。明显和明显的失败 比微妙和不被注意的失败更容易 支持。
  • 假设您想要 "show a message to the user"。这将需要将此对象紧密耦合到表示层,这违背了面向对象的原则并使代码非常僵硬且难以维护。

这个对象做一件事,而且只做那一件事。如果它的调用方式使其不能做那件事,则异常是预期的且适当的失败条件。

如果您正确地抛出和捕获异常,那么在程序中处理异常是一种很好的做法。但在这里这是微不足道的事情,不需要抛出和反对,因为你可以通过简单的控制台打印来限制它。

在这种情况下两者的工作方式相同。

当有许多内部函数调用并且您当时不知道哪个会导致异常时,抛出异常会非常有用。

顺便说一下,你不认为这些是错误的。

如果(最小 >= 最大) throw new WrongElementValueException("Min must be greater than max!");// 你正在打印的最小值应该大于最大值并且你正在检查相同值。

else if (min < 0f || max < 0f)
  throw new WrongElementValueException("Min/max must be greater than 0!");
else if (min > 100f || max > 100f)
  throw new WrongElementValueException("Min/max must be lesser than 0!");

我注意到已经有 ArgumentOutOfRangeException 正是为这种情况定义的。

if (min >= max)
  throw new WrongElementValueException("Min must be greater than max!");

这绝对应该是一个 ArgumentException,但是如果 WrongElementValueException 继承自 ArgumentException,那就没问题了。

您的总体方法是合理的。我会考虑更进一步:

HasValue = true;

为什么允许 class 永远没有价值。考虑是否添加:

public Element(float min, float max)
{
  SetRange(min, max);
}

现在您永远无法拥有一个没有设置值的实例,并且可以完全摆脱 HasValue

请注意,我将其从 float? 更改为 float。您可能会被建议在整个 class 期间都这样做。否则,如果您需要 MinMax 为空的情况(因此不想摆脱 HasValue),您需要在 SetRange:

public void SetRange(float? min, float? max)
{
    if (min < 0f || max < 0f || min > 100f || max > 100f)
      throw new ArgumentOutOfRangeException();
    if (min >= max)
      throw new WrongElementValueException("Min must be greater than max!");
    Min = min;
    Max = max;
    if(min.HasValue && max.HasValue)
    {
        Average = (min + max)/2f;
        HasValue = true;
    }
    else
    {
      Average = null;
      HasValue = false;
    }
}

(我通常更喜欢 doubledouble? 而不是 floatfloat?,除非你有充分的理由。

顺便说一句,我们通常使用 "exception handling" 来讨论使用此代码的代码如何处理您的代码抛出异常这一事实,而最好的做法取决于该代码的上下文代码而不是此代码。