为了避免所有我可以在谷歌上搜索到的标准答案,我将提供一个你们都可以随意攻击的例子。
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结构更清晰,并且不会破坏方法所做的任何契约。
恕我直言,“永远不要在常规程序流程中使用它们”的反应似乎并不是每个人都有,因为这种反应的力量可以证明。
还是我说错了?
我读过其他的帖子,处理各种特殊情况,但我的观点是,如果你们都是:
清晰的
尊重你的方法
拍我。
在异常之前,C语言中有setjmp和longjmp可以用来完成类似的堆栈帧展开。
然后,同样的构造被赋予了一个名称:“Exception”。大多数答案都依赖于这个名字的含义来争论它的用法,声称例外是为了在特殊情况下使用。这从来不是最初longjmp的意图。只是在某些情况下,您需要在许多堆栈帧之间中断控制流。
异常稍微更通用一些,因为您也可以在同一个堆栈框架中使用它们。我认为这与goto的类比是错误的。Gotos是紧密耦合的一对(setjmp和longjmp也是如此)。异常遵循松散耦合的发布/订阅,这要干净得多!因此,在相同的堆栈框架中使用它们与使用gotos几乎不是一回事。
混淆的第三个来源与它们是被检查的异常还是未检查的异常有关。当然,在控制流和许多其他事情上使用未检查异常似乎特别糟糕。
然而,受控异常对于控制流来说是很好的,一旦你克服了所有维多利亚式的困扰,并生活了一点。
我最喜欢的用法是在一长段代码中使用throw new Success()序列,它不断尝试一件事,直到找到它要找的东西。每一件事——每一段逻辑——都可能有任意的嵌套,因此break也会像任何类型的条件测试一样出现。如果-否则模式是脆弱的。如果我编辑了一个else,或者以其他方式弄乱了语法,那么就有一个毛茸茸的bug。
使用throw new Success()线性化代码流。我使用本地定义的成功类(当然是勾选的),这样如果我忘记捕获它,代码将无法编译。我没有捕捉到另一种方法的成功。
有时我的代码会逐一检查,只有在一切正常的情况下才会成功。在这种情况下,我使用throw new Failure()进行了类似的线性化。
使用单独的函数会破坏划分的自然级别。所以返回解不是最优的。出于认知上的原因,我更喜欢在一个地方放一两页代码。我不相信超精细分割的代码。
除非有一个热点,否则jvm或编译器做什么对我来说不太重要。我不相信编译器有任何根本原因不检测本地抛出和捕获的异常,而只是在机器代码级别上将它们视为非常有效的goto。
至于在控制流的函数之间使用它们——即在常见情况下而不是特殊情况下——我不明白它们为什么会比多次中断、条件测试、返回来遍历三个堆栈帧而不是仅仅恢复堆栈指针的效率更低。
我个人并没有跨堆栈框架使用这种模式,我可以理解它需要复杂的设计才能做到这一点。但适当地使用它应该没问题。
最后,关于令人惊讶的新手程序员,这不是一个令人信服的理由。如果你温和地引导他们练习,他们就会爱上它。我记得c++曾经让C程序员感到惊讶和害怕。
我的经验法则是:
如果您可以做任何事情来从错误中恢复,捕获异常
如果这个错误是很常见的(例如。用户尝试使用错误的密码登录),使用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(值类,结构或枚举)来进行错误检查。
希望我正确理解了你的问题:)
在异常之前,C语言中有setjmp和longjmp可以用来完成类似的堆栈帧展开。
然后,同样的构造被赋予了一个名称:“Exception”。大多数答案都依赖于这个名字的含义来争论它的用法,声称例外是为了在特殊情况下使用。这从来不是最初longjmp的意图。只是在某些情况下,您需要在许多堆栈帧之间中断控制流。
异常稍微更通用一些,因为您也可以在同一个堆栈框架中使用它们。我认为这与goto的类比是错误的。Gotos是紧密耦合的一对(setjmp和longjmp也是如此)。异常遵循松散耦合的发布/订阅,这要干净得多!因此,在相同的堆栈框架中使用它们与使用gotos几乎不是一回事。
混淆的第三个来源与它们是被检查的异常还是未检查的异常有关。当然,在控制流和许多其他事情上使用未检查异常似乎特别糟糕。
然而,受控异常对于控制流来说是很好的,一旦你克服了所有维多利亚式的困扰,并生活了一点。
我最喜欢的用法是在一长段代码中使用throw new Success()序列,它不断尝试一件事,直到找到它要找的东西。每一件事——每一段逻辑——都可能有任意的嵌套,因此break也会像任何类型的条件测试一样出现。如果-否则模式是脆弱的。如果我编辑了一个else,或者以其他方式弄乱了语法,那么就有一个毛茸茸的bug。
使用throw new Success()线性化代码流。我使用本地定义的成功类(当然是勾选的),这样如果我忘记捕获它,代码将无法编译。我没有捕捉到另一种方法的成功。
有时我的代码会逐一检查,只有在一切正常的情况下才会成功。在这种情况下,我使用throw new Failure()进行了类似的线性化。
使用单独的函数会破坏划分的自然级别。所以返回解不是最优的。出于认知上的原因,我更喜欢在一个地方放一两页代码。我不相信超精细分割的代码。
除非有一个热点,否则jvm或编译器做什么对我来说不太重要。我不相信编译器有任何根本原因不检测本地抛出和捕获的异常,而只是在机器代码级别上将它们视为非常有效的goto。
至于在控制流的函数之间使用它们——即在常见情况下而不是特殊情况下——我不明白它们为什么会比多次中断、条件测试、返回来遍历三个堆栈帧而不是仅仅恢复堆栈指针的效率更低。
我个人并没有跨堆栈框架使用这种模式,我可以理解它需要复杂的设计才能做到这一点。但适当地使用它应该没问题。
最后,关于令人惊讶的新手程序员,这不是一个令人信服的理由。如果你温和地引导他们练习,他们就会爱上它。我记得c++曾经让C程序员感到惊讶和害怕。