如果不使用异常,哪种技术是正确的

本文关键字:技术 异常 如果不 | 更新日期: 2023-09-27 18:11:27

假设我有一个这样的类

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)。显然,在我的情况下(如果处理一个对象列表),我可以像这样使用smth

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();
    }
}

或者一些验证器类,我猜还有其他一些东西。所以我只是想为自己制定一个标准——哪种技术更好,什么时候……

如果不使用异常,哪种技术是正确的

让我们直接开始吧;异常可能"很慢",但这正是为什么只在特殊情况下使用它们的原因。

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

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

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

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

在这种情况下,常见的方法是引发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的传递是否在范围内

的有用链接:

    创建自定义属性(c#编程指南)c#中如何使用Lambda表达式获取属性名

我试着给一些流程如何决定在这里使用:

如果你不希望出现频繁的"无效"情况,那么一定要使用异常。

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

如果你需要最大的性能,如果它是可接受的,你可以记录你的setter,并声明所有超出范围的值都被忽略。

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

如果静默忽略不正确的值不是一个选项,添加日志记录或IsValid标志,就像你的例子一样。但是要注意,您将检测/检查问题的责任转移给了调用者。异常更好,因为你必须处理它们,否则你会直接受到惩罚。

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

我希望这些提示对你有帮助。

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

例如:

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

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

坚持

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

如果您想验证从UI接收到的用户输入,那么我建议您使用Validator方法,其中您有能力通知用户期望的内容。

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