两个例外管理问题

时间:2012-02-16 11:38:06

标签: c# exception exception-handling

我对网站的异常管理有几个问题:

    在catch块中的
  • 我可以使用一个带有句柄异常方法的静态类来处理异常,如下所示:

       catch (Exception ex)
        {
            throw ExceptionHandler.HandleException(...);
        }
    

    其中ExceptionHandler.HandleException是一个返回System.Exception类型变量的静态方法。这是一个好习惯吗?这种方法有任何可能的问题吗?它是线程安全的吗?

  • 在我的应用程序中,我有一个由业务层调用的DAL层,并且UI调用了业务层。那么,重新抛出所有自定义异常是一个好习惯,这样它们就会冒出来直到显示它们的UI,而System.Exception类型会被记录,我会在catch块中抛出一个自定义异常吗? 例如在DAL和业务层中如此:

        catch (CustomExceptionBase ex)
        {
            throw;
        }
        catch (Exception sysEx)
        {
            ICustomExceptionBase ex = new SysException(sysEx);
            ex.Handle();
            throw BusinessException("Some problem while serving your request");
        }
    

    在UI层中如此

        catch (CustomExceptionBase ex)
        {
            //when custom exception bubbles up; code to display text to user on screen
        }
        catch (Exception sysEx)
        {
            ICustomExceptionBase ex = new SysException(sysEx);
            ex.Handle();
            //display error on screen;
        }
    

    这里CustomExceptionBase实现ICustomExceptionBase并继承Exception。 SysException& BusinessException都继承自CustomExceptionBase。

感谢您的时间......

修改 在系统中重新抛出的意图。异常块是这样的,如果有一个致命的错误,如数据库连接丢失或类似的东西,那么我将其记录为技术服务台并返回一个票号并重新抛出相同的内容,以便用户知道发生了什么事情错了,这是你要跟进的参考号。对于DAL图层或业务层中的所有自定义例外,我只是将其冒充到显示文本的UI。

4 个答案:

答案 0 :(得分:4)

我怀疑一些答案至少完全取决于你的架构。在第一种情况下,这一切都取决于ExceptionHandler.HandleException究竟做了什么。它是根据某些标准生成新的异常还是仅返回原始异常?

它的线程安全性与否完全取决于它的实现。例如,在以下简单的情况下,我会说它是线程安全的:

public static Exception ExceptionHandler.HandleException(Exception ex)
{
    return ex;
}

在其他情况下,它可能很容易不是线程安全的。例如:

public static string message;
public static Exception ExceptionHandler.HandleException(Exception ex)
{
    message = ex.ToString;
    sleep(2000);
    return new Exception(message);
}

后一个例子显然具有在另一个线程处于睡眠状态时由另一个线程更改的消息变量的范围。

至于第二个......应该在有意义的情况下处理异常。没有硬性规定。如果代码的某些部分可以实现从异常中恢复(或者愿意跳过它),那么在那时捕获它而不是更早。如果异常真的是致命的,那么没有什么应该试图抓住它并假装其他方式,所以你应该让它冒泡到顶部并做一些事情,例如警告你的用户事情已经崩溃,你需要重启或者其他什么。 / p>

所以真的取决于你的自定义异常是什么意思。如果他们只是表示“你想重试这个”,那么这与“数据完整性已被泄露:0 == 1”的异常不同。这两个都可能是自定义的,所以它真的可以让你决定在哪里处理事情。

答案 1 :(得分:1)

是的,您可以在catch块中调用静态异常处理程序,只要您不引用任何静态变量,它就可能是线程安全的。

您应该查看Microsoft的企业库。它具有几乎相同的设计,但使用web.config中定义的异常策略来控制如何冒泡,包装或丢弃异常。结合应用程序日志记录块,您就可以获得完整的解决方案。

答案 2 :(得分:1)

本身没有任何技术问题可以使用静态方法来处理异常/重新抛出异常,但是从最佳实践的角度来看,只有一种神奇地“处理”异常的方法会让我觉得它是潜在的代码气味。例外是他们的名字特殊,每个个案需要考虑进入它,以确保你做正确的事情,所以我发现你的HandleException方法总是做一些合理的事情。

作为一个极端的例子,我知道一个这样的应用程序,其中几乎每个方法都包含在try-catch块中,并调用静态异常处理程序方法,该方法抛出了通用MyApplicationException类型。这是一个非常糟糕的主意:

  • 它使代码混乱
  • 这使得理解堆栈跟踪变得更加困难
  • 这使得调用者很难捕获和处理特定的异常类型
  • 它使一个例外比以前更大的性能损失

我最喜欢的是一种未实现的方法,看起来有点像这样:

void SomeException()
{
    try
    {
        throw new NotImplementedException();
    }
    catch(Exception ex)
    {
        throw ExceptionHandler.HandleException(...);
    }
}

当然,最糟糕的是完全没有头脑。正如我之前所说的异常是例外 - 每个try ... catch块需要仔细考虑并考虑它应该如何表现,使用通用HandleException方法是一个立即警告标志,这可能不是'在这种情况下。

重新抛出异常

一般来说,您应该只在两种情况下重新抛出异常:

  • 如果要向异常添加上下文信息(例如正在处理的当前文件的名称)
  • 当您必须捕获异常以处理某些特定情况时,例如handling an "out of disk space" error

    catch (IOException ex)
    {
        long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
        if (win32ErrorCode == ERROR_HANDLE_DISK_FULL || win32ErrorCode == ERROR_DISK_FULL)
        {
            // Specific "out of disk space" error handling code
        }
        else
        {
            throw;
        }
    }
    

“冒泡”(即捕捉和重新抛出异常而不对其进行任何操作)是完全不必要的 - 这就是已经设计好的所有例外情况!

处理异常

其他人说“应该在有意义的地方处理例外情况”,我甚至自己也提出了这个建议,但事后我不认为这是特别有用的建议! :)

人们通常的意思是您应该处理特定原因的例外情况,并且应该根据原因选择应用程序中处理该例外的位置。

例如,如果要显示错误消息以通知用户如果获得访问被拒绝错误,则他们无权修改文件,那么您的UI代码中可能有一个特定的try-catch块这样做:

catch (IOException ex)
{
    long win32ErrorCode = Marshal.GetHRForException(ex) & 0xFFFF;
    if (win32ErrorCode == ERROR_ACCESS_DENIED)
    {
        // Display "access denied error"
    }
    else
    {
        throw;
    }
}

请注意,这对于我们希望处理的这一个案例非常具体 - 它仅捕获对感兴趣的特定异常类型,并且执行其他检查以过滤到特定情况我们是感兴趣的。

或者,如果您要记录未处理的错误或向用户正常显示错误消息而不是IIS 505错误屏幕,那么执行此操作的位置可以是Global.asax,也可以是自定义错误页面 - ASP.Net Custom Error Pages

我的观点是,在处理异常时,我们正在仔细考虑我们想要在应用程序功能方面实现的目标(例如重试逻辑,错误消息,日志记录等......)以及具体实现异常处理逻辑以最有针对性的方式解决这些需求 - 没有神奇的异常处理框架,也没有样板代码。

尽可能避免异常!

我通常发现最好的策略只是尽可能避免异常!例如,如果您的页面分析用户输入数字,并且您希望在输入愚蠢值时显示验证消息,则预先验证您的输入而不是捕获异常:

<强>为:

void DoSomething()
{
    int age = int.Parse(ageTextBox.Text);
    if (age < 0)
    {
        throw new ArgumentOutOfRangeException("age must be positive");
    }
    if (age >= 1000)
    {
        throw new ArgumentOutOfRangeException("age must be less than 1000");
    }
}

void Button_Click(object sender, EventArgs e)
{
    try
    {
        DoSomething();
    }
    catch (Exception ex)
    {
        DisplayError(ex.Message);
    }
}

不可

void Button_Click(object sender, EventArgs e)
{
    int age;
    if (!int.TryParse(ageTextBox.Text, out age))
    {
        DisplayError("Invalid age entered");
    }
    if (age < 0)
    {
        DisplayError("age must be positive");
    }
    if (age >= 1000)
    {
        DisplayError("age must be less than 1000");
    }

    DoSomething();
}

用户一直输入无效数据 - 处理这是真正的应用程序逻辑,不应该属于异常处理 - 它肯定不是我称之为“例外”的事件。

当然这并不总是可行的,但是我发现使用这种策略可以简化代码并更容易遵循应用程序逻辑。

答案 3 :(得分:0)

首先我们需要考虑Exception类型,在任何业务异常中都可以是BusinessException或Technical / SystemException。

  1. 在BusinessException中,我们可以发送带有错误的自定义异常 的信息。

  2. 在技术/系统异常中,我们不应该处理它,而是让它     弹出到UI layer.UI可以决定应该显示什么错误     例外情况。

  3. 在您的方法中,如果您处理异常或抛出自定义     呼叫追踪丢失的例外情况。