Exception 为什么不使用异常作为常规控制流?
为了避免我在谷歌搜索到的所有标准答案,我将提供一个你们都可以随意攻击的例子 C#和Java(以及其他太多)有很多类型,其中一些是我根本不喜欢的“溢出”行为(例如Exception 为什么不使用异常作为常规控制流?,exception,language-agnostic,Exception,Language Agnostic,为了避免我在谷歌搜索到的所有标准答案,我将提供一个你们都可以随意攻击的例子 C#和Java(以及其他太多)有很多类型,其中一些是我根本不喜欢的“溢出”行为(例如type.MaxValue+type.SmallestValue==type.MinValue例如:int.MaxValue+1==int.MinValue) 但是,看到我邪恶的本性,我将通过将这种行为扩展到,比方说重写的DateTime类型,对这种伤害进行一些侮辱。(我知道,DateTime在.NET中是密封的,但是为了这个例子,我使用
type.MaxValue+type.SmallestValue==type.MinValue
例如:int.MaxValue+1==int.MinValue
)
但是,看到我邪恶的本性,我将通过将这种行为扩展到,比方说重写的DateTime
类型,对这种伤害进行一些侮辱。(我知道,DateTime
在.NET中是密封的,但是为了这个例子,我使用了一种与C#完全相似的伪语言,只是DateTime没有密封)
被重写的Add
方法:
//
///以时间跨度递增此日期,但在
///超过了datetime的最大值。
///
///要(尝试)添加的时间跨度
///日期,随给定的时间跨度递增。
///如果超过DateTime.MaxValue,则总和将“溢出”,并且
///从DateTime.MinValue继续。
///
公共日期时间覆盖添加(TimeSpan ts)
{
尝试
{
返回基。添加(ts);
}
捕获(ArgumentOutOfRangeException nb)
{
//计算超出最大值的程度
//常规程序流
TimeSpan saldo=ts-(base.MaxValue-此);
return DateTime.MinValue.Add(saldo)
}
捕获(任何其他异常)
{
//“真实”异常处理。
}
}
当然,if也可以很容易地解决这个问题,但事实是,我不明白为什么不能使用异常(从逻辑上讲,我可以看出,当性能是一个问题时,在某些情况下应该避免异常)
我认为在许多情况下,它们比结构更清晰,并且不破坏该方法正在订立的任何合同
在我看来,每个人似乎都有一种“永远不要将它们用于常规程序流”的反应,这种反应的力度并不能证明这一点
还是我错了
我读过其他帖子,涉及各种特殊情况,但我的观点是,如果你们都是:
开枪打我。异常基本上是非本地的
goto
语句,以及后者的所有后果。将异常用于流控制违反了一个规则,使程序难以读取(请记住,程序首先是为程序员编写的)
此外,这不是编译器供应商所期望的。他们希望很少抛出异常,而且他们通常让抛出的代码效率很低。抛出异常是.NET中最昂贵的操作之一
但是,有些语言(特别是Python)使用异常作为流控制结构。例如,如果没有其他项,迭代器将引发StopIteration
异常。即使是标准的语言结构(如
)也依赖于此。我的经验法则是:
- 如果您可以采取任何措施从错误中恢复,请捕获异常
- 如果错误非常常见(例如,用户试图使用错误的密码登录),请使用ReturnValue
- 如果您无法从错误中恢复,请将其保持为未捕获状态(或者在主捕获程序中捕获它,以半正常地关闭应用程序)
我看到的异常问题是从纯语法的角度来看的(我非常确定性能开销是最小的)。我不喜欢到处碰壁
举个例子:
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(valueclasses、structs或enum)来进行错误检查
希望我能正确理解你的问题:)你有没有试过调试一个在正常操作过程中每秒引发五次异常的程序
我有
这个程序相当复杂(它是一个分布式计算服务器),在程序的一侧稍加修改就可以很容易地在完全不同的地方破坏某些东西
我希望我可以启动程序并等待异常发生,但在正常操作过程中,启动期间大约有200个异常
我的观点:如果在正常情况下使用异常,如何定位异常(即异常)情况?
当然,还有其他强有力的理由不太使用异常,特别是在性能方面,我认为可以将异常用于流控制。然而,这项技术还有另一面。创建异常代价高昂,因为它们必须创建堆栈跟踪。因此,如果您想更频繁地使用异常,而不仅仅是发出异常情况的信号,那么您必须确保构建堆栈跟踪不会对性能产生负面影响
降低创建异常成本的最佳方法是重写fillInStackTrace()方法,如下所示:
public Throwable fillInStackTrace() { return this; }
这样的异常将不会填充stacktraces。我觉得您的示例没有任何错误。相反,忽略被调用函数引发的异常将是一种罪恶
在JVM中,抛出异常并不昂贵,只使用新的xyzException(…)创建异常,因为后者涉及堆栈遍历。因此,如果您提前创建了一些异常,您可以多次抛出它们,而无需支付任何费用。当然,这种方式不能将数据与异常一起传递,但我认为这无论如何都是一件坏事。标准的anwser是异常不是常规的
-1 - x lesser then MinX
-2 - x greater then MaxX
-3 - y lesser then MinY
/**
* Adds two positive numbers.
*
* @param addend1 greater than zero
* @param addend2 greater than zero
* @throws AdditionException if addend1 or addend2 is less than or equal to zero
*/
int addPositiveNumbers(int addend1, int addend2) throws AdditionException{
if( addend1 <= 0 ){
throw new AdditionException("addend1 is <= 0");
}
else if( addend2 <= 0 ){
throw new AdditionException("addend2 is <= 0");
}
return addend1 + addend2;
}
/**
* Adds two positive numbers.
*
* @param addend1 greater than zero
* @param addend2 greater than zero
*/
public int addPositiveNumbers(int addend1, int addend2) {
if( addend1 <= 0 ){
throw new IllegalArgumentException("addend1 is <= 0");
}
else if( addend2 <= 0 ){
throw new IllegalArgumentException("addend2 is <= 0");
}
return addend1 + addend2;
}
adr lr,next_instr
b subroutine
b handle_exception
next_instr:
if (PerformCheckSucceeded())
DoSomething();
try
{
PerformCheckSucceeded();
DoSomething();
}
catch
{
}