2

我的技术负责人坚持这种异常机制:

try
{
    DoSth();
}
catch (OurException)
{
    throw;
}
catch (Exception ex)
{
    Util.Log(ex.Message, "1242"); // 1242 is unique to this catch block
    throw new OurException(ex);
}

这里的 1242 是我们处理 OurException 以外的异常的 catch 方法的标识符。项目中的每个 catch 块都必须有一个唯一的标识符,这样我们就可以通过查看日志来知道异常发生在哪里。

对于每个方法,我们都必须捕获 OurException 并抛出它。如果抛出了其他类型的异常,我们必须在重新抛出它之前记录它并用 OurException 屏蔽它。

这是一个合理的方法吗?如果有的话,有什么更好的选择?

编辑:我被告知堆栈跟踪在发布模式下不会产生有意义的结果。您是否建议捕获和抛出通用异常可以?

编辑2:谢谢大家。我用你的回答作为我反对这个论点的一部分,但我被告知你没有足够的经验,不知道如何处理现实生活中的情况。我必须走这条路。

4

13 回答 13

5

您还可以查看异常处理应用程序块。

我在一些项目中使用过它,它非常有用。特别是如果您想稍后更改异常处理的工作方式以及要捕获的信息。

于 2008-12-23T17:59:30.327 回答
3

我认为使用堆栈跟踪会比任何标识符更直观。

至于自定义异常,为什么不这样做呢?

try
{
DoSth();
}
catch(Exception ex)
{
Util.Log(ex.Message, ex.StackTrace);
if(ex is OurException) throw ex;
else throw new OurException(ex); // use the original exception as the InnerException
}

另外,我不确定您为什么要在处理完异常后重新抛出异常-您能解释一下背后的原因吗?

@Ali A - 一个非常有效的观点,所以请允许我重新措辞 - 为什么要重新抛出异常而不是在这里完成对它的处理?

编辑:

与其重新抛出,为什么不这样做呢?

try
{
DoSth();
}
catch(Exception ex)
{
Util.HandleException(ex);
}

Util.HandleException:

public static void HandleException(ex)
{
Util.Log(ex); // Util.Log should log the message and stack trace of the passed exception as well as any InnerExceptions - remember, than can be several nested InnerExceptions.

// Any additional handling logic, such as exiting the application, or showing the user an error message (or redirecting in a web app)
}

这样,您就知道每个异常只记录一次,并且您不会将它们放回野外以造成任何额外的破坏。

于 2008-12-23T17:57:33.483 回答
2

拥有OurException它有点奇怪。通常,您希望有专门的 catch 块,然后是最后一个,即捕获泛型的那个Exception是您进行日志记录的地方:

try 
{
    DoSomething();
}
catch (DivideByZeroException)
{
    // handle it here, maybe rethrow it, whatever
}
// more catch blocks
catch (Exception)
{
    // oops, this is unexpected, so lets log it
}

但你的所作所为会奏效。我确实相信1242应该去。这是一种打印方法、文件名和行号的方法,您可以使用它来代替。我自己没有尝试过,但看起来不错:

    [Conditional("DEBUG")]
    public static void DebugPrintTrace()
    {
        StackTrace st = new StackTrace(true);
        StackFrame sf = st.GetFrame(1); // this gets the caller's frame, not this one
        Console.WriteLine("Trace "
            + sf.GetMethod().Name + " "
            + sf.GetFileName() + ":"
            + sf.GetFileLineNumber() + "\n");
    } 
于 2008-12-23T18:12:30.300 回答
1

我有两种类型的异常:可修复异常和致命异常。如果某个对象抛出可修复的异常,这意味着发生错误但对象没有损坏,可以重新使用。如果某个对象抛出致命异常,这意味着对象状态已损坏,任何使用对象的尝试都会导致新的错误。

更新:可能会尽快处理所有异常,并尽可能靠近错误源。例如,如果存储在集合中的对象抛出致命异常,异常处理程序只是从集合中删除该对象并将其删除,而不是整个对象集合。

于 2008-12-23T18:03:55.047 回答
1

据我了解,异常的目的是传播意外错误。如果您将它捕获到与抛出它的方法足够接近,那么您将更多地了解如何处理它。

您的示例是捕获意外错误,然后将其重新抛出链。这不是处理异常,而是将其包装到您自己的中。

但是您的包装似乎并没有为异常增加任何价值,甚至可能使事情变得混乱。

一个极端的例子:

void a() {
  try {
    c();
  } catch(MyException1 ex) {
    throw;
  } catch(Exception ex) {
    log(ex);
    throw new MyException1(ex);
  }
}

void b() {
  try {
    a();
  } catch(MyException2 ex) {
    throw;
  } catch(Exception ex) {
    log(ex);
    throw new MyException2(ex);
  }
}

请注意,在前面的示例中,原始异常被记录了两次。它包含在两个例外中。当您多次记录相同的异常时,跟踪变得更加困难(因为日志文件变得相当大)。

当然这可能是一个极端的例子,但我很难相信你的整个应用程序只使用了一种类型的异常。它没有充分描述可能发生的所有不同类型的错误。

我个人更喜欢只在我处理它的 catch 块中记录异常。其他任何地方都可能会创建重复的日志记录。

于 2008-12-24T14:58:47.167 回答
0

我认为在 throw 线上有一个硬编码的数字不是一个好习惯,你怎么知道这个数字是否被使用?或者,下一个要抛出的错误号是哪个?也许您至少可以在枚举中列出...不确定。

于 2008-12-23T17:56:32.050 回答
0

除了那个奇怪的数字外,一切看起来都正确。

堆栈跟踪将包含您需要的信息。

于 2008-12-23T17:57:20.143 回答
0

记录堆栈跟踪会比硬编码的数字好得多。这个数字没有告诉你什么程序叫这个程序。

此外,这个数字管理起来很麻烦,而且很容易出错。

编辑:的确,stacktrace 在发布模式下并不总是产生有意义的结果,但我认为这个硬编码的数字方案也可以这样说!:-)

对于您使用的语言,编译器是否提供带有当前文件名和行号的宏?这比尝试自己滚动一个唯一的数字要好。而且,如果您不能自动执行此操作,请手动执行,或者提出一些方案来保证唯一性,而不需要集中分配数字!

于 2008-12-23T18:00:25.193 回答
0

在我看来,堆栈跟踪是处理这个问题的更好方法。如果您错误地重复使用一个号码会发生什么?

至于这是否是一个好主意,在没有更多信息的情况下,我倾向于说不。如果每个方法调用都包装在一个 try/catch 块中,那将会非常昂贵。一般来说,异常分为两个阵营——那些你可以合理预期和处理的和那些真正的错误(或至少没有成功预期)。在我看来,使用霰弹枪方法捕获所有异常似乎只是为了隐藏后者而不是帮助解决它们。如果您在顶层捕获所有异常以便您拥有的唯一记录是日志消息,则尤其如此。

于 2008-12-23T18:01:35.300 回答
0

我看不出这有什么帮助。您已经可以通过堆栈跟踪知道异常来自何处,并且您正在添加不必要的复杂性。

于 2008-12-23T18:04:26.747 回答
0

缺点:

  1. 这个数字并没有激发太多信心,堆栈跟踪要好得多

  2. 如果您的自定义异常可以在“catch(Exception ex)”上处理,为什么还要有 2 个 catch 块?

于 2008-12-23T18:06:23.310 回答
0

我认为这是一个糟糕的模式,因为您可以在异常中随时获得有关整个调用堆栈的信息,因此无需通过记录并重新抛出异常来伪处理异常。仅在您真正可以解决问题的地方捕获异常。

于 2008-12-23T18:28:45.637 回答
0

我的应用程序是在发布模式下构建的,我使用异常处理应用程序块,所以我从来没有任何catch ex代码块,它被 EHAB 捕获,它本身会写入包含我需要的所有信息的日志文件;堆栈跟踪等,时间等。

唯一的问题是如果有人放

catch ex
{
    //do stuff
    throw ex;
}

因为这将清除堆栈跟踪。

于 2008-12-25T15:15:34.540 回答