我们正在努力使用策略来正确处理应用程序中的异常。这是我们的目标(总结):
我们推出了一个涉及通用应用程序特定异常的解决方案,并且在一段代码中就是这样的:
try {
// Do whatever
}
catch(ArgumentNullException ane)
{
// Handle, optinally log and continue
}
catch(AppSpecificException)
{
// Rethrow, don't log, don't do anything else
throw;
}
catch(Exception e)
{
// Log, encapsulate (so that it won't be logged again) and throw
Logger.Log("Really bad thing", e.Message, e);
throw new AppSpecificException(e)
}
记录所有异常,然后转为AppSpecificException,以便不再记录它。最终它将到达最后一个处理它的事件处理程序,如果必须的话。
我对异常处理模式没有太多经验......这是解决我们目标的好方法吗?它有任何重大缺点或大红色警告吗?
注意:其中一个缺点是在第一次捕获后你失去了处理特定异常的能力(如果你调用一个调用另一个方法的方法而第二个抛出一个异常你无法处理它)但我发现我从来没有这样做过......我只处理一个深度的异常......
答案 0 :(得分:60)
如果您在异常第一次抛出异常时记录异常,则不会记录完整的堆栈跟踪。
处理异常(即修复它们),尽可能接近它们被抛出的时间。尽快收集有关上下文的信息。但允许异常传播到实际可以处理的位置。日志记录是最后一种处理方式,因此应该在应用程序子系统的外层中进行。
这样就不需要使用特定于应用程序的异常作为标记来记录不应该开始捕获的异常。
答案 1 :(得分:6)
不要记录异常然后重新抛出 - 调用者有责任处理/记录您生成的任何异常。
仅捕获异常以处理它(例如记录它),或添加特定于上下文的信息。
答案 2 :(得分:0)
这是解决异常处理问题的一种非常常见的方法(从更具体到更不具体)。
请记住,如果要捕获特定问题,有一个通用的ApplicationSpecific异常来捕获该应用程序/方法中发生的所有事情并不是一个好主意。 最后尝试使用更具体的例外来扩展它。
重新抛出异常是好的,更好的方法是声明抛出某些异常并让调用者处理它们的方法。这样您就必须创建更少的代码,并且可以集中控制一些控件。
答案 3 :(得分:0)
解决堆栈跟踪问题的第一个选项:
class AppSpecificException : ApplicationException
{
public string SpecificTrace { get; private set; }
public string SpecificMessage { get; private set; }
public AppSpecificException(string message, Exception innerException)
{
SpecificMessage = message;
SpecificTrace = innerException.StackTrace;
}
}
我不得不写一个例子来理解问题并检查堆栈跟踪问题,这是我的代码,注意button2_click方法,最后我的文本框显示崩溃字符串和堆栈跟踪:
private String internalValue;
private void Operation1(String pField)
{
if (pField == null) throw new ArgumentNullException("pField");
internalValue = pField;
}
private void Operation2(Object pField)
{
if (pField == null) throw new ArgumentNullException("pField");
internalValue = Convert.ToInt32(pField).ToString();
}
private void Operation3(String pField)
{
if (pField == null) throw new ArgumentNullException("pField");
internalValue = pField;
Operation2(-1);
}
/// <exception cref="AppSpecificException"><c>AppSpecificException</c>.</exception>
private void button1_Click(object sender, EventArgs e)
{
try
{
Operation1("One");
Operation2("Two");
Operation3("Three");
MessageBox.Show(internalValue);
}
catch (ArgumentNullException ex)
{
textBoxException.Text = ex.Message + (char) 13 + (char) 10 + ex.StackTrace;
}
catch (AppSpecificException ex)
{
//textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace;
throw;
}
catch (Exception ex)
{
textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace;
throw new AppSpecificException("crash", ex);
}
}
private void button2_Click(object sender, EventArgs e)
{
try
{
button1_Click(sender, e);
}
catch (AppSpecificException ex)
{
textBoxException.Text = ex.SpecificMessage + (char) 13 + (char) 10 + ex.SpecificTrace;
}
}
答案 4 :(得分:0)
尽量避免创建新的异常和重新抛出,因为抛出异常会将堆栈跟踪设置为抛出异常的位置。做一个简单的投掷。请参阅Eric Lippert博客上的Too Much Reuse。
答案 5 :(得分:0)
我建议您更多地考虑一下您要用于“处理”的模式
如果您的处理模式归结为记录或重新抛出,则最终将记录重新抛出的错误。所以最后,它只是错误记录。如果您正在使用ASP.NET,请使用elmah,因此至少您的代码不包含在try / catch-and-log样板中。
只有几种方法可以“处理”仅仅记录日志的错误。
重新尝试。 (注意无限循环)
等等并重新尝试。
尝试不同但等效的技术(无法连接到http?尝试连接https)。
建立缺少的条件(创建抛出FolderNotFoundException的文件夹)
忽略错误 - 请三思而后行,只有当错误不是真正的问题时才有意义,例如第三方库是否警告您某个条件不适用。
答案 6 :(得分:-1)
关于异常处理的一个很好的解决方案是使用拦截。但是,您必须验证是否可以根据体系结构将此模式应用于您的应用程序:拦截需要容器。
原则是通过在方法上使用属性(自定义)将异常处理分解为方法,然后使用容器初始化实例。容器将通过反射来代理这些实例:它的被调用实例拦截器。 你只需要像往常一样通过这些实例调用你的方法,让拦截机制完成你在方法之前或之后编写的工作。
请注意,您可以在方法中添加try catch来管理拦截器中未受管理的特定异常。
Unity拦截:http://msdn.microsoft.com/en-us/library/dd140045.aspx