Pet*_*ter 184 language-agnostic exception
为了避免我可以用Google搜索的所有标准答案,我将提供一个你可以随意攻击的例子.
C#和Java(和太多的人)有很多类型的一些"溢出"的行为,我不都不像(如type.MaxValue + type.SmallestValue == type.MinValue例如: int.MaxValue + 1 == int.MinValue).
但是,看到我的恶性,我会通过扩展这种行为来增加对这种伤害的一些侮辱,让我们说一个Overridden 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.
}
}
Run Code Online (Sandbox Code Playgroud)
当然,if可以解决这个问题同样容易,但事实仍然是我只是不明白为什么你不能使用异常(从逻辑上讲,我可以看到,当性能是一个问题,在某些情况下应该避免异常).
我认为在很多情况下,它们比if-structures更清晰,并且不会违反该方法的任何合同.
恕我直言"从不使用它们进行常规程序流程"反应,每个人似乎都没有那么好的建设,因为这种反应的力量可以证明是合理的.
还是我弄错了?
我已经阅读过其他帖子,处理各种特殊情况,但我的观点是,如果你们两个都是:
拍我
Bra*_*ann 162
您是否曾尝试调试在正常操作过程中每秒产生五个异常的程序?
我有.
该程序非常复杂(它是一个分布式计算服务器),在程序的一侧稍作修改就可以轻松地在完全不同的地方破坏某些东西.
我希望我可以启动程序并等待异常发生,但在正常运行过程中启动期间有大约200个异常
我的观点:如果你在正常情况下使用例外,你如何找到异常(即例外情况)?
当然,还有其他强有力的理由不使用过多的异常,特别是在性能方面
Ant*_*lev 152
例外基本上是非本地goto声明,具有后者的所有后果.使用流控制的异常违反了最不惊讶的原则,使程序难以阅读(请记住,程序首先是为程序员编写的).
而且,这不是编译器供应商所期望的.他们希望很少抛出异常,他们通常会让throw代码效率很低.抛出异常是.NET中最昂贵的操作之一.
但是,某些语言(特别是Python)使用异常作为流控制结构.例如,StopIteration如果没有其他项,则迭代器会引发异常.甚至标准语言结构(例如for)依赖于此.
cwa*_*wap 26
我的经验法则是:
我看到异常的问题是从纯语法的角度来看(我很确定性能开销很小).我不喜欢整个地方的试用块.
举个例子:
try
{
DoSomeMethod(); //Can throw Exception1
DoSomeOtherMethod(); //Can throw Exception1 and Exception2
}
catch(Exception1)
{
//Okay something messed up, but is it SomeMethod or SomeOtherMethod?
}
Run Code Online (Sandbox Code Playgroud)
..另一个例子可能是当你需要使用工厂为句柄分配东西时,该工厂可能抛出异常:
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.. :(
Run Code Online (Sandbox Code Playgroud)
洙,就个人而言,我认为你应该保持罕见的错误条件异常(内存不足等),并使用returnvalues(valueclasses,结构或枚举)做你的错误检查来代替.
希望我理解你的问题正确:)
Pet*_*ter 24
对许多答案的第一反应:
你是为程序员写的,也是最不惊讶的原则
当然!但是,如果不是一直都不清楚.
它应该不是令人惊讶的例如:除(对于我来说)(在康拉德和其他人处),除(1/x)捕获(divisionByZero)比任何更明确.不期望这种编程的事实纯粹是传统的,实际上仍然是相关的.也许在我的例子中,if会更清楚.
但是DivisionByZero和FileNotFound比ifs更清楚.
当然,如果它的性能较差并且每秒需要大量的时间,你当然应该避免它,但我仍然没有找到任何有理由避免全部设计.
至于最小惊讶的原则:这里存在循环推理的危险:假设整个社区使用了糟糕的设计,这种设计将成为预料之中!因此,原则不能是一个圣杯,应该仔细斟酌.
正常情况下的例外情况,您如何定位异常情况(即特殊情况)?
在很多反应中...... 像这样闪耀着低谷.抓住他们,不是吗?你的方法应该清晰,记录良好,并且能够签订合同.我必须承认,我没有得到这个问题.
调试所有异常:相同,有时只是因为不使用异常的设计很常见.我的问题是:为什么它首先是常见的?
nec*_*cer 16
例外之前,在C,有setjmp和longjmp可用于完成栈帧的相似展开.
然后给同一个结构命名:"Exception".大多数答案都依赖于这个名称的含义来争论它的用法.它旨在用于特殊条件.这从未反映在原版中longjmp.在某些情况下,您需要在多个堆栈帧中打破控制流.
异常略微更通用,因为您也可以在同一堆栈框架中使用它们.这与goto我认为错误的类比提出了类比.Gotos是一个紧密耦合的对(也是如此)setjmp和longjmpExceptions遵循松散耦合的发布/订阅更加清晰!
第三个混淆的来源是它们是被检查还是未经检查的例外.当然,未经检查的异常似乎特别难以用于控制流.
一旦你克服了维多利亚时代的所有挂断并且活了一点,那么检查的异常非常适合控制流程.
我最喜欢的用法是goto在一段长代码中的序列,它会一个接一个地尝试,直到找到它要查找的内容.每一件事 - 每一条逻辑 - 都可能有任意的嵌套,所以throw new Success()也可以进行任何条件测试.该break模式是脆.如果我if-else以其他方式编辑或弄乱语法,那么就会出现毛病.
使用else 线性化代码流.我使用本地定义的throw new Success()类 - 当然检查 - 所以如果我忘记捕获它,代码将无法编译.我没有抓住另一种方法Success.
有时我的代码会检查一个接一个的东西,只有在一切正常时才会成功.在这种情况下,我有一个类似的线性化使用Success.
使用单独的功能会混淆自然级别的区域化.所以throw new Failure()解决方案不是最优的.出于认知原因,我更喜欢在一个地方有一两页代码.我不相信超细分代码.
除非存在热点,否则JVM或编译器所做的事与我的关系不大.我无法相信编译器没有任何根本原因来检测本地抛出并捕获异常并简单地将它们视为return机器代码级别的高效s.
至于在控制流的功能中使用它们 - 即对于常见情况而不是特殊情况 - 我看不出它们如何效率低于多次中断,条件测试,返回通过三个堆栈帧而不仅仅是恢复堆栈指针.
我个人不会在堆栈帧中使用该模式,我可以看到如何优雅地设计复杂性.但是谨慎地使用它应该没问题.
最后,对于令人惊讶的处女程序员来说,这并不是一个令人信服的理由.如果你轻轻地将它们介绍给练习,他们就会学会喜欢它.我记得C++曾经惊讶并吓跑了C程序员.
mou*_*iel 11
标准的anwser是异常不规则,应该在特殊情况下使用.
原因之一,这对我很重要,就是当我读了try-catch在我维护软件或调试控制结构,我试图找出原因,原来编码器使用的异常,而不是处理一个的if-else结构.我期待找到一个好的答案.
请记住,您不仅要为计算机编写代码,还要为其他编码器编写代码.有一个与异常处理程序相关的语义,你不能因为机器不介意而丢弃它.
Josh Bloch在Effective Java中广泛讨论了这个主题.他的建议很有启发性,也应该适用于.Net(细节除外).
特别是,例外情况应用于特殊情况.其原因主要是与可用性相关.对于最大可用的给定方法,其输入和输出条件应该被最大限度地约束.
例如,第二种方法比第一种方法更容易使用:
/**
* 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;
}
Run Code Online (Sandbox Code Playgroud)
在任何一种情况下,您都需要检查以确保调用者正确使用您的API.但在第二种情况下,你需要它(隐含地).如果用户没有读取javadoc,仍然会抛出软异常,但是:
- 您不需要记录它.
- 您不需要对其进行测试(取决于您的单元测试策略的积极程度).
- 您不需要调用者处理三个用例.
地面点是异常不应该用作返回代码,主要是因为你不仅复杂了你的API,还复杂了调用者的API.
当然,做正确的事是有代价的.成本是每个人都需要了解他们需要阅读并遵循文档.希望无论如何都是这样.
我认为您可以使用Exceptions进行流量控制.然而,这种技术有另一面.创建异常是一件昂贵的事情,因为他们必须创建堆栈跟踪.因此,如果您希望更频繁地使用异常,而不仅仅是发出特殊情况,则必须确保构建堆栈跟踪不会对性能产生负面影响.
降低创建异常的成本的最佳方法是覆盖fillInStackTrace()方法,如下所示:
public Throwable fillInStackTrace() { return this; }
Run Code Online (Sandbox Code Playgroud)
这样的异常将没有填充堆栈跟踪.
我真的没有看到你如何控制你引用的代码中的程序流程.除了ArgumentOutOfRange异常之外,你永远不会看到另一个异常.(所以你的第二个catch子句永远不会被击中).你所做的只是使用极其昂贵的投掷来模仿if语句.
此外,你不会执行更加险恶的操作,你只是抛出一个异常,因为它被捕获到其他地方以执行流控制.你实际上是处理一个特例.
除了陈述的原因之外,不使用异常进行流控制的一个原因是它会使调试过程大大复杂化。
例如,当我试图追踪 VS 中的错误时,我通常会打开“中断所有异常”。如果您使用异常进行流控制,那么我将定期中断调试器,并且将不得不继续忽略这些非异常异常,直到找到真正的问题为止。这很可能会让人抓狂!!
| 归档时间: |
|
| 查看次数: |
42913 次 |
| 最近记录: |