使用try / catch作为测试的错误表单?

时间:2011-03-08 15:33:27

标签: c# performance forms try-catch

我觉得这是,但我想证实 - 做以下事情是不好的形式:

try
{
    SqlUpload(table);
}
catch(PrimaryKeyException pke)
{   
          DeleteDuplicatesInTable(table);
          SqlUpload(table);
}
catch(Exception ex)
{
    Console.Write(ex);
}

如果表没有重复项,这样做是否更有可能节省效率,或者最好还是运行delete duplicates位? (我假设只要表格中没有重复项就会上传表格的条件)。另外,考虑到try-catch语句对性能的影响,以这种方式执行它会更快吗?

我为这个例子的原始性质道歉,但这只是为了说明一点。

7 个答案:

答案 0 :(得分:3)

可以在事务管理中正确使用异常,但在此示例中并非如此。在我第一眼看来,它看起来似乎与Linq2Sql的DataContext类在调用SubmitChanges()时所做的相似。但是,这个比喻是不正确的。 (请参阅Chris Marisic对我的帖子的评论,以准确批评比较)。

关于例外

通常,如果可能遇到某些问题,您应该先检查一下。当响应真正“异常”时(意味着在正确使用的情况下它是意外的),应该使用例外。如果在完全有效的上下文中正确使用函数会引发异常,那么您可能错误地使用了异常。

摘自DataContext.SubmitChanges

此代码显示了正确使用事务管理中的例外的示例。

注意:仅仅因为微软这样做,并不自动意味着它的权利。但是,他们的代码确实有很好的跟踪记录。

      DbTransaction transaction = null;
        try
        {
            try
            {
                if (this.provider.Connection.State == ConnectionState.Open)
                {
                    this.provider.ClearConnection();
                }
                if (this.provider.Connection.State == ConnectionState.Closed)
                {
                    this.provider.Connection.Open();
                    flag = true;
                }
                transaction = this.provider.Connection.BeginTransaction(IsolationLevel.ReadCommitted);
                this.provider.Transaction = transaction;
                new ChangeProcessor(this.services, this).SubmitChanges(failureMode);
                this.AcceptChanges();
                this.provider.ClearConnection();
                transaction.Commit();
            }
            catch
            {
                if (transaction != null)
                {
                    try
                    {
                        transaction.Rollback();
                    }
                    catch
                    {
                    }
                }
                throw;
            }
            return;
        }
        finally
        {
            this.provider.Transaction = null;
            if (flag)
            {
                this.provider.Connection.Close();
            }
        }
    }

答案 1 :(得分:1)

是的,这种类型的代码在.NET中被认为是不好的形式。

你最好编写类似于

的代码
if(HasPrimaryKeyViolations(table))
    DeletePrimaryKeyViolations(table)

SqlUpload(table)

答案 2 :(得分:1)

通过阅读此代码,我会假设主键违规是一个例外情况而不是预期的 - 如果它们是我认为你应该事先删除重复项,不要依赖于预期的异常处理情况下。

答案 3 :(得分:1)

在所有常见语言/解释器/编译器中,实现异常处理以在未引发时对性能产生最小影响 - 在引擎盖下,添加异常处理程序通常只是推动堆栈上的单个值或类似的东西。添加try块通常不会对性能产生影响。

另一方面,当实际引发异常时,事情会变得非常缓慢。它是能够添加try块而不用担心令人担忧的权衡,它通常被认为是可以接受的,因为只有在其他地方出现意外情况时你才会受到性能影响。

因此,从理论上讲,如果存在期望发生的情况,请改用if。它在语义上更好,因为它向读者表明坏情况可能会不时发生(例如,用户输入一些无效的输入),而try表达你希望永远不会发生的事情(数据)来源是腐败的)。如上所述,它在性能上也会更容易。

当然,规则是由他们的例外(没有双关语意图)定义的。在实践中,有两种情况会成为错误的答案:

首先,如果您正在执行复杂的操作,例如解析文件及其全部或全部 - 如果文件中的一个字段已损坏,您希望保留整个操作。异常允许您跳出整个复杂过程,直到封装整个解析的异常处理程序。当然,你可以通过检查和返回值来丢弃解析代码并检查返回值 - 但是只是为了抛出异常并让它上升到操作的顶部,它会变得更加清晰。即使您希望输入有时会变坏,如果没有合理的方法在错误发生时准确处理错误,请使用异常让错误上升到更合适的位置来处理它。这确实是首先出现的例外情况 - 在细节中删除所有错误处理代码,并将其移至一个合并的合理位置。

其次,图书馆可能不允许您做出选择。例如,如果输入尚未经过审查,int.TryParseint.Parse的一个很好的替代方法。另一方面,库可能没有非异常抛出选项。在这种情况下,不要酿造你自己的代码来检查没有例外 - 虽然使用异常处理来检查预期的条件可能是不好的形式,它更糟糕的形式来复制库的功能,只是为了避免例外。只需使用try/catch,也可以添加关于你不想做的讽刺评论,但是图书馆的作者让你:)。

对于您的特定示例,可能坚持异常。虽然异常处理不被认为是“快速”,但它仍然比往返数据库更快;如果没有发送命令,也没有合理的方法来检查该异常。此外,在与外部系统连接时访问数据库 - 当您离开特定的受控环境时,这本身就是一个很好的理由来预期意外 - 异常处理几乎总是有意义的。

或者,更具体地说,对于您的示例,您可以考虑使用带有MERGE语句的存储过程,以使用table中的源数据进行适当更新或插入;除了对现有密钥执行delete-then-insert之外,更新在所有方面都会更友好一些。

答案 4 :(得分:0)

尝试捕获在性能上是昂贵的,因此不要将它们用作控制结构。而是在数据库级别使用触发器。

答案 5 :(得分:0)

一个问题是,在catch块中调用SqlUpload()导致的任何异常都会导致应用程序崩溃,因为没有进一步的异常处理。

答案 6 :(得分:0)

你可能会对此有一些不同的看法,但我的try...catch应该用于通常不会发生的事情(虽然有时它是不可避免的)。因此,根据该规则,您应该通过正常执行程序来询问重复项是否在表中,或者如果允许执行程序则它们不应该存在。

只是为了澄清,我说的是“正常”使用该程序,而不是“正确”使用(例如,当我测试时,重复项不会出现(正确使用),但是当客户使用它时,他们会这样做(也许)不正确但正常),所以我需要摆脱它们)。更重要的是,重复项只会以程序无法控制的方式出现(例如,有时会有人进入数据库并添加重复行(希望不是正常情况))。

但是,像重复行这样的东西很可能是其他一些bug的症状,所以你不应该掩盖它,而是试着找到根本原因,这样就不需要删除了。