哪个是使用带有foreach循环的try-catch块的最佳实践?

时间:2014-06-26 16:18:25

标签: c#

使用try {} catch {}阻止性能的最佳做法是什么?

foreach (var one in all)
{
    try
    {
        //do something
    }
    catch { }
}

或者

try
{
    foreach (var one in all)
    {
        // do something
    }
}
catch { }

7 个答案:

答案 0 :(得分:6)

没有严格的规则是公平的,这是情境性的。

这取决于你是否想要停止整个循环,如果其中一个项目导致问题,或者只是抓住那个问题并继续。

例如,如果您要向人们发送电子邮件,则不希望在发送其中一个发生异常时停止处理,但是如果您正在管理一组数据库事务并且如果其中任何一个失败则需要回滚,也许更希望在异常/问题上停止处理?

答案 1 :(得分:5)

在任何一种情况下,性能可能都相同(但如果你想确定,可以运行一些测试)。每次循环时都会发生异常检查,只是在被捕时跳到其他地方。

行为不同。

在第一个示例中,将捕获一个项目上的错误,并且循环将继续用于其余项目。

在第二个中,一旦你遇到错误,循环的其余部分将永远不会被执行。

答案 2 :(得分:5)

根据要求,这是我很酷的答案。有趣的部分将结束,所以如果您已经知道try-catch是什么,请随意滚动。 (对不起部分偏离主题)

让我们首先回答一下try-catch的概念。

为什么呢?因为这个问题表明缺乏完整的知识如何以及何时使用此功能。

什么是try-catch?或者更确切地说,什么是try-catch-finally。

(这一章也被称为:为什么你还没有用谷歌来了解它呢?)

  1. 尝试 - 可能不稳定的代码,这意味着你应该全部移动 稳定的部分。它始终执行,但没有保证 完成。

  2. Catch - 在这里您可以设置用于纠正故障的代码 发生在尝试部分。仅在发生异常时执行     试试阻止。

  3. 最后 - 它的第三部分也是最后一部分,在某些语言中可能没有     存在。它总是被执行。通常它用于释放     内存和关闭I / O流。

  4. 通常,try catch是一种将可能不稳定的代码与其他程序分开的方法。 就机器语言而言,可以缩短为将所有处理器寄存器的值放在堆栈上以防止它们损坏,然后通知环境忽略执行错误,因为它们将由代码手动处理。

    使用try-catch块的最佳做法是什么?

    根本不使用它们。使用try-catch覆盖代码意味着您希望它失败。为什么代码失败?因为写得很糟糕。 在性能和质量方面,编写不需要try-catch安全工作的代码要好得多。

    有时,特别是在使用第三方代码时,try-catch是最简单且最可靠的选项,但大多数时候在您自己的代码上使用try-catch表示设计问题。

    示例:

    1. 数据解析 - 在数据解析中使用try-catch非常非常糟糕。有很多方法可以安全地解析甚至最奇怪的数据。其中最丑陋的一种是正则表达式方法(有问题吗?使用正则表达式,问题喜欢复数)。字符串到Int转换失败?首先检查您的数据,.NET甚至提供TryParse等方法。

    2. 除零,精度问题,数值溢出 - 不要 用try-catch覆盖它,而不是升级你的代码。算术代码 应该从良好的数学方程开始。当然你可以大量修改 数学方程式运行得更快(例如 0x5f375a86),但你仍然需要良好的数学开始。

    3. 列表索引越界,堆栈溢出,分段错误, Heartbleed - 在这里你有更大的代码设计错误。那些 错误应该只是在正确编写的代码中运行 健康的环境。所有这些都出现了一个简单的错误,代码有 确保索引(内存地址)在预期的边界内。

    4. I / O错误 - 在尝试使用流之前(内存,文件, 网络),第一步是检查流是否存在(非空,文件 存在,连接打开)。然后检查流是否正确 - 是 你的索引大小?流准备好使用了吗?是它的队列/缓冲区 容量足够大的数据?所有这一切都可以不用 单尝试。特别是当你在框架下工作时(.NET, Java等。)

      当然还存​​在意外访问问题 - 老鼠 m m你的网线,硬盘驱动器融化。这里用法 try-catch不仅可以被原谅,而且应该被发生。它仍然需要 要以适当的方式完成,例如this example for files。 你不应该在try-catch中放置整个流操作代码, 而是使用内置方法来检查其状态。

    5. 糟糕的外部代码 - 当你开始使用可怕的代码库时, 没有任何纠正方法(欢迎来到企业界), try-catch通常只是保护其余代码的方法。但是 再次,只有直接危险的代码(调用可怕的 写得不好的库中的函数应该放在try-catch中。

    6. 那么什么时候应该使用try-catch,什么时候不应该?

      可以用非常简单的问题回答。

      我可以更正代码而不需要try-catch吗?

      是?然后删除try-catch并修复代码。

      没有?然后在try-catch中打包不稳定的部分并提供良好的错误处理。

      如何处理Catch中的异常?

      第一步是了解可能发生的异常类型。现代环境提供了在classe中隔离异常的简便方法。尽可能地捕获最具体的异常。做I / O?捕获I / O.做数学?捕捉算术算法。

      用户应该知道什么?

      只有用户可以控制的内容:

      • 网络错误 - 检查电缆。
      • 文件I / O错误 - 格式c:。
      • 内存不足 - 升级。

      其他例外情况只会告知用户你的代码写得有多糟糕,所以坚持神秘的内部错误。

      在循环中或在其外部尝试捕捉?

      正如很多人所说,这个问题没有明确的答案。这一切都取决于你提交的代码。

      一般规则可能是: 原子任务,每次迭代都是独立的 - 循环内的try-catch。 链计算,每次迭代都取决于之前的迭代 - 围绕循环尝试捕获。

      是多少?

      Foreach循环不保证按顺序执行。听起来很奇怪,几乎从未发生,但仍有可能。如果你使用foreach创建它的任务(数据集操作),那么你可能想要在它周围放置try-catch。但正如所解释的那样,你应该试着不要经常使用try-catch。

      有趣的部分!

      亲爱的读者,这篇文章的真正原因只有你几行!

      根据Francine DeGrood Taylor的要求,我会在有趣的部分写一点。 请记住,正如约阿希姆·伊萨克森(Joachim Isaksson)所注意到的那样,乍一看是非常奇怪的。

      虽然这部分将重点介绍.NET,但它可以应用于其他JIT编译器,甚至可以部分应用于汇编。

      那么..围绕循环的try-catch有可能加快速度吗? 它没有任何意义!错误处理意味着额外的计算!

      检查此Stackoverflow问题:Try-catch speeding up my code? 你可以在那里阅读.NET特定的东西,在这里我将尝试着重于如何滥用它。 请记住,这个问题是从2012年开始的,所以它也可以“纠正”#34; (这不是一个bug,它是一个功能!)在当前的.NET版本中。

      如上所述,try-catch正在将代码段与其余代码分开。 分离过程以与方法类似的方式工作,因此您可以在单独的方法中使用重度计算来代替try-catch。

      分隔代码如何加快速度?寄存器。网络比HDD,HDD慢于RAM,与超高速CPU缓存相比,RAM是慢速的。还有CPU注册表,它们嘲笑Cache的速度有多慢。

      分离代码通常意味着释放所有通用寄存器 - 而这正是try-catch正在做的事情。或者更确切地说,由于try-catch,JIT正在做什么。

      JIT最突出的缺陷是缺乏预知。它看到循环,它编译循环。当它最终注意到循环将执行数千次并且拥有使CPU发出吱吱声的计算时,释放寄存器为时已晚。因此,必须编译循环中的代码以使用寄存器的剩余部分。

      即使一个额外的寄存器也可以产生巨大的性能提升。每次内存访问都非常长,这意味着CPU可能会在相当长的时间内未使用。虽然现在我们得到了无序执行,可爱的管道和预取,但仍然存在阻止代码停止的阻塞操作。

      现在让我们来谈谈为什么x86糟透了,与x64相比是垃圾。编译为x64时没有出现链接SE问题中的try-catch速度增益,为什么?

      因为开始时没有速度增益。所有存在的都是由蹩脚的JIT输出引起的速度损失(经典编译器没有这个问题)。 Try-catch主要是偶然纠正了JIT行为。

      certain tasks创建了x86寄存器。 x64体系结构的大小增加了一倍,但它仍然无法改变这样的事实,即在进行循环时你必须牺牲CX,其他寄存器也是如此(除了可怜的孤儿BX)。

      那么为什么x64如此棒?它拥有8个额外的64位宽寄存器,没有任何特定用途。你可以用它们做任何事情。理论上不仅与x88寄存器相似,而且确实适用于任何事情。 8个64位寄存器意味着8个64位变量直接存储在CPU寄存器而不是RAM中,没有任何数学问题(这需要AX和DX经常用于结果)。 64位还意味着什么? x86可以使Int适合寄存器,x64可以适合Long。如果数学块有空寄存器可以工作,它可以完成大部分工作而不会触及内存。而且这是真正的速度提升。

      但它不是结束!您也可以滥用缓存。 Cache越接近CPU,它就越快,但它也会越小(成本和物理尺寸都是限制)。如果您要优化数据集以立即填写缓存,例如。大小为L1的一半的日期块,留下另一半的代码和CPU在缓存中找到的任何内容(除非你使用汇编,否则你无法真正优化它,在高级语言中你必须" guestimate")。通常每个(物理)内核都有自己的L1内存,这意味着您可以同时处理多个缓存的块(但它不会总是通过创建线程来节省开销)。

      值得一提的是旧的Pascal / Delphi使用了#16; 16位恐龙"在几个重要功能的32位处理器时代(这使它们比C / C ++中的32位处理器慢两倍)。所以喜欢你的CPU寄存器,即使是可怜的旧BX。他们非常感激。

      为了增加一点,因为这已经变得相当疯狂了,为什么C#/ Java可以同时比本机代码更慢更快? JIT就是答案,框架代码(IL)被转换为机器语言,这意味着长计算块将像C / C ++的本机代码一样执行。但请记住,您可以轻松地在.NET中使用本机组件(在Java中,您可以通过尝试来疯狂)。对于足够复杂的计算,您可以通过本机代码的速度增益来切换托管本机模式的开销(并且可以通过asm注入来提升本机代码)。

答案 3 :(得分:0)

这取决于你想要实现的目标:)第一个将尝试循环中的每个函数,即使其中一个将失败,休息将运行...第二个将中止整个循环甚至一个错误...

答案 4 :(得分:0)

在第一个例子中,循环在捕获发生后继续(除非你告诉它断开)。也许你想在你需要将错误数据收集到List(catch中要做的事情)的情况下使用它,以便在最后向某人发送电子邮件并且不想停止如果出现问题,整个过程。

在第二个例子中,如果你希望它在出现问题时立即点击中断以便你进行分析,那么它将阻止循环的其余部分发生。

答案 5 :(得分:0)

您应该更好地了解您想要的行为而不是性能。考虑是否希望能够在异常发生时继续循环,以及在哪里进行清理。在某些情况下,您可能希望在循环内外捕获两者异常。

try...catch对效果的影响非常小。你做的大多数事情实际上可能导致异常需要比设置捕获异常所花费的时间长很多,因此在大多数情况下,性能差异可以忽略不计。

在任何可能存在可衡量的性能差异的情况下,你都会在循环中做很少的工作。通常在这种情况下你会想要循环外的try...catch,因为循环中没有任何东西需要清理。

答案 6 :(得分:0)

对于大多数应用程序而言,性能差异可以忽略不计。

但问题是,如果一个失败,你是否想继续处理循环中的其余项目。如果是,请使用内部foreach,否则使用单个外部循环。