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

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结构更清晰,并且不会破坏方法所做的任何契约。

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

还是我说错了?

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

清晰的 尊重你的方法

拍我。


当前回答

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

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

其他回答

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

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

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

我的经验法则是:

如果您可以做任何事情来从错误中恢复,捕获异常 如果这个错误是很常见的(例如。用户尝试使用错误的密码登录),使用returnvalues 如果您不能做任何事情来从错误中恢复,那么就让它不被捕获(或者在主捕获器中捕获它,以半优雅地关闭应用程序)

我认为异常的问题纯粹是从语法的角度来看的(我非常确定性能开销是最小的)。我不喜欢到处都是尝试块。

举个例子:

try
{
   DoSomeMethod();  //Can throw Exception1
   DoSomeOtherMethod();  //Can throw Exception1 and Exception2
}
catch(Exception1)
{
   //Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}

. .另一个例子是,当你需要使用工厂将一些东西赋值给一个句柄时,该工厂可能会抛出异常:

Class1 myInstance;
try
{
   myInstance = Class1Factory.Build();
}
catch(SomeException)
{
   // Couldn't instantiate class, do something else..
}
myInstance.BestMethodEver();   // Will throw a compile-time error, saying that myInstance is uninitalized, which it potentially is.. :(

就我个人而言,我认为你应该为罕见的错误条件(内存不足等)保留异常,而使用returnvalues(值类,结构或枚举)来进行错误检查。

希望我正确理解了你的问题:)

您是否曾经尝试调试一个在正常操作过程中每秒引发5个异常的程序?

我有。

该程序相当复杂(它是一个分布式计算服务器),在程序的一侧稍作修改,就可能很容易破坏完全不同的地方的某些东西。

我希望我可以启动程序,然后等待异常发生,但是在正常的操作过程中,在启动过程中大约有200个异常

我的观点是:如果你在正常情况下使用异常,你如何定位异常情况?

当然,还有其他强有力的理由不要过多使用异常,特别是在性能方面

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

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

public Throwable fillInStackTrace() { return this; }

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

性能如何?在负载测试一个。net web应用程序时,我们在每个web服务器上模拟了100个用户,直到我们修复了一个常见的异常,这个数字增加到500个用户。