良好的异常处理设计模式

3
我在底层方法中的每个方法都有异常处理代码。
throw new Exception("The error that happens");

有没有办法避免在每个方法中反复编写这段代码?

我正试图编写自己的代码,而不使用任何日志框架。

private void TopLevelMethod()
{
    try
    {
        SomeMethod();
    }
    catch (Exception ex)
    {
        // Log/report exception/display to user etc.
    }
}

private void SomeMethod()
{
    TestPartA();
    TestPartB();
    TestPartC();
    TestPartD();
}

private void TestPartA()
{
    // Do some testing...
    try
    {
        if (somethingBadHappens)
        {
            throw new Exception("The error that happens");
        }
    }
    catch (Exception)
    {
        // Cleanup here. If no cleanup is possible, 
        // do not catch the exception here, i.e., 
        // try...catch would not be necessary in this method.

        // Re-throw the original exception.
        throw;
    }
}

private void TestPartB()
{
    // No need for try...catch because we can't do any cleanup for this method.
    if (somethingshappens)
    {
        throw new Exception("The error that happens");
    }
}

你知道 throwthrow new 的区别吗?在这里看一下,并且进行一些谷歌搜索:https://dev59.com/lXA75IYBdhLWcg3w-OZA - MethodMan
谢谢。我知道这个。我正在尝试寻找一些好的设计模式,用于异常管理,并带有任何集中式管理器。 - priya
Graggito:能否请您提供一些示例代码?谢谢。 - priya
你正在使用哪种演示层?例如,ASP.Net MVC、Web Form、WPF或Win Form。 - Win
你的问题既过于宽泛,又基于个人观点。但是无论如何,有一点始终如一 - 你的异常处理将取决于你的整体应用程序设计。 - T.S.
显示剩余2条评论
3个回答

3

只有在想要对错误进行有意义的操作时才捕获它们,例如:

  1. 使用框架异常(例如SqlException)。ADO.NET从不传递套接字级别的错误。它会传递有意义的SQL错误代码。
  2. 清理
  3. 实际响应(例如重试或插入默认值)

记录日志几乎从来不合适。顶层处理程序应该记录日志。当然,不是每个路径中的方法都应该记录日志。这会让日志和代码变得混乱。不要这样做。

简单地不要吞噬错误信息并让错误冒泡出来。这样就没有理由在每个地方插入本地记录错误的代码了。


1

如果您喜欢使用函数式编程的代码风格,一种方法是使用回调错误回调。例如:

    private void SomeMethod()
    {
        // do something
    }
     public bool Execute(Action act, Action<Exception> onErrorCallback)
        {
            var res = true;
            try
            {
                act();
            }
            catch (Exception ex)
            {
                res = false;
                onErrorCallback(ex);
            }
            return res;
        }

使用如下代码运行:Execute
   var successfull = true;
   successfull &= Execute(SomeMethod, (ex) => {  /* clean up */ });
   successfull &= Execute(SomeMethod, (ex) => {  /* clean up */ });
   successfull &= Execute(SomeMethod, (ex) => {  /* clean up */ });
   successfull &= Execute(SomeMethod, (ex) => {  /* clean up */ });
   if (!successfull)
       ; // show user or something else

1
你刚刚将 JavaScript 中最让人讨厌的东西介绍给了 C#。回调函数用于传递结果。 - usr
还有,你带来了 ON ERROR RESUME NEXT :) - usr

0
Graffito:请您提供一个示例代码。谢谢...
您的代码已经重构:
private void TopLevelMethod()
{ 
    List<string> errors=new List<string>() ;
    if (!SomeMethod(errors)) { /* Log/report errors/display to user etc. */ }
}

private bool SomeMethod(List<string> errors)
{
    return TestPartA(errors) && TestPartB(errors) && TestPartC(errors) && TestPartD(errors);
}

private bool TestPartA(List<string> errors)
{
  bool result = true ;
  try 
  {
    // Do some testing...
    if (somethingBadHappens) { result=false; errors.Add("The error that happens"); }
  }
  catch (Exception ex) { errors.Add("Error in TestPartA: "+Ex.Exception.Message.ToString()) ; }
  return result ;
}

private bool TestPartB(List<string> errors)
{
  bool result = true ;
  // Do some testing...
  if (somethingBadHappens) { result = false ; errors.Add("The error that happens"); }
  return result ;
}

1
将异常转换为返回值会失去异常所带来的好处。此外,此代码片段中的所有方法都依赖于和改变共享状态(列表),这是另一个缺点。如果我在代码审查中看到这样的代码,我会不通过。 - usr
"usr"说:“将异常转换为返回值会失去异常带来的好处。” 对于许多异常,返回值是适当的(例如,文件写入失败)。有些异常应该保留(例如,检测到前一个过程提供的异常数据)。还要注意,异常处理很慢。例如,基准测试“b=int.tryParse(s,out i) vs try { i=int.Parse(s) ; } catch {}”。 - Graffito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接