我的技术主管坚持这种例外机制:
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这里是catch方法的标识符,我们处理除OurException以外的异常。项目中的每个catch块都必须具有唯一的标识符,以便我们通过查看日志来了解异常发生的位置。
对于每个方法,我们必须捕获OurException并抛出它。如果抛出其他类型的异常,我们必须在重新抛出之前记录它并通过OurException对其进行掩码。
这是一种合理的方法吗?如果有,有什么更好的选择?
编辑:我被告知堆栈跟踪在发布模式下不会产生有意义的结果。 您是否建议捕获并抛出一般异常?
Edit2:谢谢大家。我用你的答案作为我反对此论点的一部分,但我被告知你没有足够的经验,也不知道如何应对现实生活中的情况。我必须走这条路。
答案 0 :(得分:5)
您还可以查看Exception Handling Application block.
我在一些项目中使用它,它非常有用。特别是如果您想稍后更改异常处理的工作方式以及要捕获的信息。
答案 1 :(得分: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)
}
通过这种方式,您知道每个异常只会被记录一次,并且您不会将它们扔回野外以造成任何额外的破坏。
答案 2 :(得分: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");
}
答案 3 :(得分:1)
我有两种类型的例外:可修复异常和致命异常。如果某个对象抛出可修复的异常,则表示发生错误但对象未被损坏且可以重复使用。如果某个对象抛出致命异常,则表示对象状态已损坏,并且任何使用对象的尝试都将导致新错误。
更新:可能会尽快处理所有异常,并尽可能接近错误源。例如,如果存储在集合中的对象抛出致命异常,则异常处理程序只是从集合中删除该对象并将其删除,而不是所有对象集合。
答案 4 :(得分: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块中记录异常。其他任何地方都可能只创建重复的日志记录。
答案 5 :(得分:0)
我认为在投掷线上有一个硬编码的数字并不是一个好习惯,你怎么知道这个数字是否被使用?或者,这是下一个要抛出的错误号码?也许你至少可以在枚举中列出......不确定。
答案 6 :(得分:0)
除了那个奇怪的号码外,一切看起来都很正确。
堆栈跟踪将包含您需要的信息。
答案 7 :(得分:0)
记录堆栈跟踪比硬编码数字好很多。这个数字不会告诉你什么例程叫做这个。
此数字也很难管理,而且容易出错。
编辑:堆栈跟踪确实总是在发布模式下产生有意义的结果,但我认为这个硬编码方案也是如此! : - )
使用您正在使用的语言,编译器是否为宏提供了当前文件名和行号?这比尝试自己滚动一个唯一的数字更好。并且,如果您不能自动执行此操作,请手动执行,或者提出一些方案以保证不需要对数字进行集中分配的唯一性!
答案 8 :(得分:0)
对我来说,堆栈跟踪是一种更好的方法来处理这个问题。如果您错误地重复使用数字会怎样?
至于这是否是一个好主意,在没有任何更多信息的情况下,我倾向于拒绝。如果每个方法调用都包含在try / catch块中,那么它将非常昂贵。一般来说,例外属于两个阵营 - 您可以合理预期和处理的阵营以及那些真正的错误(或者至少没有成功预期)。在我看来,使用霰弹枪方法来捕获所有异常似乎只是为了隐藏后者而不是帮助解决它们。如果在顶级您捕获了所有异常以便您拥有的唯一记录是日志消息,则尤其如此。
答案 9 :(得分:0)
我看不出这有多大帮助。您已经可以知道堆栈跟踪的异常来源,并且您正在添加不必要的复杂程度。
答案 10 :(得分:0)
缺点:
这个数字不会激发人们的信心,堆栈跟踪要好得多
如果可以在“catch(Exception ex)”上处理自定义异常,为什么有2个catch块 “?
答案 11 :(得分:0)
如果您打算使用这种模式,那么我希望在创建它时看到更多信息添加到OurException,而不仅仅是它包装的异常,例如DoSth()应该做什么的描述,沿着任何相关的上下文信息,例如传递的参数。然后,当你真实地捕获OurException时,除了已经包含在异常中的内容之外,你还有一些有用的信息。
答案 12 :(得分:0)
我认为这是一个糟糕的模式,因为你有关于异常中可用的整个调用堆栈的信息,不需要通过记录和重新抛出它来伪处理异常。只能在你真正可以解决问题的地方发现异常。
答案 13 :(得分:0)
我的应用程序是在发布模式下构建的,我使用异常处理应用程序块,所以我从来没有任何catch ex
代码块,它被EHAB捕获,它本身写入所有日志文件我需要的信息;堆栈跟踪等,时间等。
唯一的问题是如果有人放了
catch ex
{
//do stuff
throw ex;
}
因为这将消除堆栈跟踪。