【问题标题】:Which technique is correct if not using exceptions?如果不使用异常,哪种技术是正确的?
【发布时间】:2011-10-14 18:19:15
【问题描述】:

假设我有这样的课程

class A
{
    private int _x; //must be between [-10 10]
    private int _y; //must be between [10 20]
}

使用属性我有几个变量的变体

public int X
{
    get { return _x; }
    set
    {
        if (!(value >= -10 && value <= 10))
            throw new Exception("Some error text");
        _x = value;
    }
}

public int X
{
    get { return _x; }
    set
    {
        if (!(value >= -10 && value <= 10))
            return;
        _x = value;
    }
}

(与 _y 相同)。
在第一种情况下,我不确定我是否要使用异常(它们很慢并且有其他已知问题)。其次,我要为不使用它们的值的模糊性而付出代价(以某种不同的方式用于 _x 和 _y)。 显然我可以在我的情况下(如果处理对象列表)那样使用

public bool IsValid = true;
//...
public int X
{
    get { return _x; }
    set
    {
        if (!(value >= -10 && value <= 10))
        {
            IsValid = false;
            return;
        }
        _x = value;
    }
}
class AWrapper
{
    public List<A> AList {get; set;}
    public AWrapper(List<A> list)
    {
        AList = list.Where(x => x.IsValid == true).ToList();
    }

}

或一些validator class 或……我猜还有一些其他的东西。 所以我只想为自己制定标准——哪种技术更好,什么时候……

【问题讨论】:

  • 抛出异常。最好尽快知道操作失败,而不是仅仅为了弄清楚这一点而检查一些变量。如果您完全熟悉 C,就像必须在 errno 和异常之间进行选择一样。并不总是最好用。

标签: c# .net


【解决方案1】:

让我们直接开始吧;异常可能“缓慢”,但这正是您只在特殊情况下使用它们的原因。

在您的情况下,如果超出给定范围的数字是异常的(即,永远不应该发生),那么您可以抛出异常。但是,如果这类似于用户输入,那么用户填写可接受范围之外的值肯定是不是例外!在这种情况下,您需要验证输入的值是否在可接受的范围内。

关于这个解决方案的另一点:

set
{
    if (!(value >= -10 && value <= 10))
        return;
    _x = value;
}

IMO,这同样糟糕(如果不是更糟的话)。原因是如果我设置X=10,我希望当我阅读X 时它具有我刚刚设置的值。

【讨论】:

    【解决方案2】:

    在这种情况下,常见的方法是提高InvalidArgumentException。如果您不想处理异常,可以引入自定义标志 IsValid(正如您已经提到的)。

    为了保持一个属性范围,我建议为此目的引入自定义属性并通过它标记属性,例如:

    public bool IsValid { }
    
    [ValueRange(Max = 10, Min = 5)]
    public int X
    {
        set
        {
           this.ValidateValueRange(this.X, value);
        }
    }
    
    private bool ValidateValueRange(...)
    {
       // 1. Get property value (see link below regarding retrieving a property)
       // 2. Get ValueRange attribute values
       // 3. Update this.IsValid
       // 4. Return ...
    }
    

    然后实现单个方法检查传入的value是否在范围内。

    有用的链接:

    【讨论】:

      【解决方案3】:

      我尝试给出一些流程如何决定在这里使用什么:

      如果您预计不会出现频繁的“无效”情况,请务必使用异常。

      set
      {
          if (!(value >= -10 && value <= 10))
              throw new Exception("Some error text");
          _x = value;
      }
      

      否则,如果您需要最大性能并且可以接受,您可以记录您的设置器并声明所有超出范围的值都将被忽略。

      set
      {
          if (value >= -10 && value <= 10)
              _x = value;
          // else optionally log something
      }
      

      如果不能选择默默地忽略不正确的值,请添加日志记录或 IsValid 标志,如您的示例中所示。但请注意,您将检测/检查问题的责任转移给调用者。例外更好,因为您必须处理它们,否则您将受到直接惩罚。

      另一个答案中提供的自定义属性的使用只是实现检查的另一种方式。

      我希望这些提示会有所帮助。

      【讨论】:

        【解决方案4】:

        .Net 中检查值是否有效的标准模式是创建一个首先检查值的方法,让类的用户调用它并处理结果,然后抛出异常。

        例如:

        public bool IsValueValid(xxx)
        {
        
        }
        
        public void SetValue(xxx)
        {
            if(!this.IsValueValid())
            {
                throw Exception();
            }
        }
        

        通常必须存储值的类不知道如何处理无效值,调用者的工作是知道如何处理该值。

        【讨论】:

          【解决方案5】:

          坚持

          public int X
          {
              get { return _x; }
              set
              {
                  if (!(value >= -10 && value <= 10))
                      throw new Exception("Some error text");
                  _x = value;
              }
          }
          

          【讨论】:

            【解决方案6】:

            如果您想验证从 UI 收到的用户输入,那么我建议您使用 Validator 方法,您可以告知用户预期的内容。

            在您决定不显示异常并继续使用任何安全/默认值或使用自定义异常中断流程的其他情况下,这是一种权衡。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-01-17
              • 2013-10-07
              • 2011-03-26
              • 2010-10-07
              • 2013-10-16
              • 2010-09-12
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多