为了避免所有我可以在谷歌上搜索到的标准答案,我将提供一个你们都可以随意攻击的例子。

c#和Java(以及其他很多语言)有很多类型,有些“溢出”行为我一点也不喜欢(例如type。MaxValue +类型。SmallestValue ==类型。MinValue,例如int。MaxValue + 1 = int.MinValue)。

但是,鉴于我的邪恶本性,我将通过将此行为扩展为重写DateTime类型来对这种伤害进行侮辱。(我知道DateTime在. net中是密封的,但为了这个例子,我使用了一种与c#完全相似的伪语言,除了DateTime没有密封之外)。

被覆盖的Add方法:

/// <summary>
/// Increments this date with a timespan, but loops when
/// the maximum value for datetime is exceeded.
/// </summary>
/// <param name="ts">The timespan to (try to) add</param>
/// <returns>The Date, incremented with the given timespan. 
/// If DateTime.MaxValue is exceeded, the sum wil 'overflow' and 
/// continue from DateTime.MinValue. 
/// </returns>
public DateTime override Add(TimeSpan ts) 
{
    try
    {                
        return base.Add(ts);
    }
    catch (ArgumentOutOfRangeException nb)
    {
        // calculate how much the MaxValue is exceeded
        // regular program flow
        TimeSpan saldo = ts - (base.MaxValue - this);
        return DateTime.MinValue.Add(saldo)                         
    }
    catch(Exception anyOther) 
    {
        // 'real' exception handling.
    }
}

当然,如果可以很容易地解决这个问题,但事实仍然是,我不明白为什么不能使用异常(从逻辑上讲,我可以看到,当性能是一个问题时,在某些情况下应该避免异常)。

我认为在许多情况下,它们比if结构更清晰,并且不会破坏方法所做的任何契约。

恕我直言,“永远不要在常规程序流程中使用它们”的反应似乎并不是每个人都有,因为这种反应的力量可以证明。

还是我说错了?

我读过其他的帖子,处理各种特殊情况,但我的观点是,如果你们都是:

清晰的 尊重你的方法

拍我。


当前回答

一般来说,在低级别处理异常本身并没有什么问题。异常是一个有效的消息,它提供了一个操作为什么不能执行的很多细节。如果你能接受,你就应该接受。

一般来说,如果你知道有很高的失败概率,你可以检查…你应该去检查一下…即if(obj != null) obj.method()

在你的情况下,我对c#库不够熟悉,不知道date time是否有一种简单的方法来检查时间戳是否越界。如果是,只需调用If (.isvalid(ts)) 否则,您的代码基本上没问题。

所以,归根结底,这取决于哪种方式可以创建更干净的代码……如果防止预期异常的操作比处理异常更复杂;那么你就有我的权限来处理异常,而不是到处创建复杂的守卫。

其他回答

标准的回答是,异常是不正常的,应该在特殊情况下使用。

其中一个对我来说很重要的原因是,当我在维护或调试的软件中读取try-catch控制结构时,我试图找出为什么最初的编码器使用异常处理而不是if-else结构。我希望能找到一个好的答案。

请记住,您不仅为计算机编写代码,还为其他编码员编写代码。有一个与异常处理程序相关联的语义,您不能仅仅因为机器不介意就将其丢弃。

我认为您可以使用异常进行流控制。然而,这种技术也有另一面。创建异常是一件代价高昂的事情,因为它们必须创建堆栈跟踪。因此,如果你想更频繁地使用异常,而不仅仅是发出异常情况的信号,你必须确保构建堆栈跟踪不会对你的性能产生负面影响。

减少创建异常成本的最佳方法是重写fillInStackTrace()方法,如下所示:

public Throwable fillInStackTrace() { return this; }

这样的异常不会填充堆栈跟踪。

一个美学原因是:

“尝试”总是伴随着“成功”,而“如果”并不一定伴随着“其他”。

if (PerformCheckSucceeded())
   DoSomething();

使用try/catch,它变得更加冗长。

try
{
   PerformCheckSucceeded();
   DoSomething();
}
catch
{
}

6行代码太多了。

你可能会对Common Lisp的条件系统感兴趣,它是对异常的一种概括。因为你可以以一种可控的方式展开堆栈,也可以得到“重启”,这非常方便。

这与其他语言的最佳实践没有太大关系,但它向您展示了在您正在考虑的方向上,使用一些设计思想可以完成什么。

当然,如果您像溜溜球一样在堆栈上上下跳动,仍然存在性能考虑,但这比大多数捕获/抛出异常系统所体现的“哦,糟糕,让我们离开”这种方法要普遍得多。

让我们假设您有一个做一些计算的方法。它必须验证许多输入参数,然后返回一个大于0的数字。

使用返回值来表示验证错误,这很简单:如果方法返回的数字小于0,则发生错误。如何判断哪个参数无效?

我记得在我学习C语言的时候,很多函数返回的错误代码是这样的:

-1 - x lesser then MinX
-2 - x greater then MaxX
-3 - y lesser then MinY

etc.

它真的比抛出和捕获异常的可读性更差吗?