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

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)) 否则,您的代码基本上没问题。

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

以下是我在博客中描述的最佳实践:

抛出异常以说明软件中的意外情况。 使用返回值进行输入验证。 如果您知道如何处理库抛出的异常,请在尽可能低的级别捕获它们。 如果出现意外异常,请完全放弃当前操作。不要假装你知道如何对付他们。

异常基本上是非本地的goto语句,具有后者的所有结果。在流控制中使用异常违反了最小惊奇原则,使程序难以阅读(请记住,程序首先是为程序员编写的)。

而且,这不是编译器供应商所期望的。他们希望很少抛出异常,并且他们通常让抛出代码非常低效。抛出异常是。net中最昂贵的操作之一。

然而,一些语言(尤其是Python)使用异常作为流控制结构。例如,如果没有其他项,迭代器将引发StopIteration异常。即使是标准的语言结构(比如for)也依赖于此。

有一些通用的机制,语言可以允许一个方法退出而不返回值,并unwind到下一个“catch”块:

Have the method examine the stack frame to determine the call site, and use the metadata for the call site to find either information about a try block within the calling method, or the location where the calling method stored the address of its caller; in the latter situation, examine metadata for the caller's caller to determine in the same fashion as the immediate caller, repeating until one finds a try block or the stack is empty. This approach adds very little overhead to the no-exception case (it does preclude some optimizations) but is expensive when an exception occurs. Have the method return a "hidden" flag which distinguishes a normal return from an exception, and have the caller check that flag and branch to an "exception" routine if it's set. This routine adds 1-2 instructions to the no-exception case, but relatively little overhead when an exception occurs. Have the caller place exception-handling information or code at a fixed address relative to the stacked return address. For example, with the ARM, instead of using the instruction "BL subroutine", one could use the sequence: adr lr,next_instr b subroutine b handle_exception next_instr:

要正常退出,子例程只需执行bx lr或pop {pc};在异常退出的情况下,子例程将在执行返回之前从LR中减去4,或者使用sub LR,#4,pc(取决于ARM的变化,执行模式等)。如果调用者没有被设计为适应它,这种方法将会非常严重地故障。

A language or framework which uses checked exceptions might benefit from having those handled with a mechanism like #2 or #3 above, while unchecked exceptions are handled using #1. Although the implementation of checked exceptions in Java is rather nuisancesome, they would not be a bad concept if there were a means by which a call site could say, essentially, "This method is declared as throwing XX, but I don't expect it ever to do so; if it does, rethrow as an "unchecked" exception. In a framework where checked exceptions were handled in such fashion, they could be an effective means of flow control for things like parsing methods which in some contexts may have a high likelihood of failure, but where failure should return fundamentally different information than success. I'm unaware of any frameworks that use such a pattern, however. Instead, the more common pattern is to use the first approach above (minimal cost for the no-exception case, but high cost when exceptions are thrown) for all exceptions.

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

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

public Throwable fillInStackTrace() { return this; }

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