异常处理和安全编码的最佳实践

本文关键字:最佳 编码 安全 异常处理 | 更新日期: 2023-09-27 18:33:48

假设您正在调用类似于以下内容的方法,您知道该方法只会引发 2 个异常之一:

public static void ExceptionDemo(string input)
{
    if (input == null)
        throw new ArgumentNullException("input");
    if (input.Contains(","))
        throw new ArgumentException("input cannot contain the comma character");
    // ...
    // ... Some really impressive code here
    // ...
}
执行此操作

的方法的一个真实示例是 Member.GetUser (String(

您将使用以下哪项来调用该方法并处理异常:

方法 1(首先检查输入参数(

public static void Example1(string input)
{
    // validate the input first and make sure that the exceptions could never occur
    // no [try/catch] required
    if (input != null && !input.Contains(","))
    {
        ExceptionDemo(input);
    }
    else
    {
        Console.WriteLine("input cannot be null or contain the comma character");
    }
}

方法 2(将调用包装在尝试/捕获中(

public static void Example2(string input)
{
    // try catch block with no validation of the input
    try
    {
        ExceptionDemo(input);
    }
    catch (ArgumentNullException)
    {
        Console.WriteLine("input cannot be null");
    }
    catch (ArgumentException)
    {
        Console.WriteLine("input cannot contain the comma character");
    }
}

多年来,我已经教授了这两种方法,并想知道这种情况的一般最佳实践是什么。

更新几张海报关注的是引发异常的方法,而不是处理这些异常的方式,所以我提供了一个 .Net Framework 方法的示例,该方法的行为方式相同(Member.GetUser (String((所以,为了澄清我的问题,如果你打电话给Membership.GetUser(input)你会如何处理可能的异常,方法 1、2 或其他东西?

谢谢

异常处理和安全编码的最佳实践

这取决于,但一般来说,提出的这两种方法都不好。如前所述,在第一种情况下,您正在复制代码。在第二个中,你正在捕获异常,而没有实际做任何事情 - 甚至没有重新抛出,只是吞下它。如果您只想记录它或显示一些消息,通常您应该使用 AppDomain.UnhandledException 实现全局处理程序/记录器并在那里执行此操作;这样,您就不必用不必要的 try/catch 块污染您的代码。

这里真正的问题是,在您的特定情况下,输入为 null 或包含 ',' 是否真的是一种异常行为 - 例如,如果这是某个 GUI 输入的字符串,那么这通常不会导致异常抛出(最终用户错误应该是预期的(,并且应该得到适当的处理(例如,警告重新输入输入(。在这种情况下,使用 if 语句来验证输入是正确的方法。但是,如果输入为 null 或包含 ',' 是一种实际的异常行为(例如,指示某些内容损坏或丢失的 API 问题(,则抛出异常是可以的。在这种情况下,您可以简单地调用ExceptionDemo(input)而无需尝试/捕获。如果你想对异常做一些实际的事情(例如,以某种方式更改输入(,那么使用 try/catch。

调用方不应假设他们正在调用的代码的任何内容。

你的第一个例子很糟糕,因为你在复制代码:调用方执行几乎(string.INOE() vs string == null(与被调用方相同的检查(直到它们中的任何一个改变(。

第二个示例非常糟糕,因为它忽略了抛出的异常并给出了自己的解释。

像往常一样:这取决于。如果你有一个正确分层的应用程序,其中方法调用位于你的UI层中,你确实希望只捕获该方法引发的异常:你将希望向用户显示这些错误。

这取决于调用 ExceptionDemo 的次数以及它暴露给谁。如果它被广泛使用,则当您知道(并记录(ExceptionDemo无论如何都会进行检查时,您就不想在调用ExceptionDemo之前检查条件。

鉴于返回类型为无效,如果输入错误,将 ExceptionDemo 更改为不起作用怎么办?

(您是否注意到您在方法 1 中更严格 - 空字符串不是有效的输入,但在方法 2 中是(

我会推荐标准和通用结构如下:

public static void Operation(object input)
{
    try
    {
        ValidateInput(input);
        //Do Operation
    }
    catch (MySpecificException subSubExceptionType) //Catch most specific exceptions
    {
        //Log or process exception
        throw;
    }
    catch (MySpecificException subExceptionType) //Catch specific exception
    {
        //Log or process exception
    }
    catch (Exception exceptionType) //Catch most generic exception
    {
        //Log or process exception
    }
    finally
    {
        //Release the resources
    }
}
private static void ValidateInput(object input)
{
    if(input == null)
        throw new NoNullAllowedException();
    //Check if properties of input are as expected. If not as expected then throw specific exception with specific message
}