C# 异常处理

在这里,您将学习使用try,catch和finally块在C#中进行异常处理的知识。

必须处理应用程序中的异常,以防止程序崩溃和意外结果,记录异常并继续执行其他功能。C#提供内置支持使用 try,catch和finally块 来处理异常。

语法:

try
{
    // 将代码放在这里可能会引发异常
}
catch
{
    // 在这里处理异常
}
finally
{
    // 最终清理代码
}

try 块:任何可能引发异常的可疑代码都应放在一个try{ }块中。在执行过程中,如果发生异常,则控制流跳至第一个匹配catch块。

catch :catch块是异常处理程序块,您可以在其中执行一些操作,例如记录和审核异常。catch块采用异常类型的参数,您可以使用该参数获取异常的详细信息。

finally :finally无论是否引发异常,都将始终执行该块。通常,finally应该使用一个块来释放资源,例如,关闭在try块中打开的任何流或文件对象。

如果您输入非数字字符,则以下内容可能会引发异常。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Enter a number: ");

        var num = int.Parse(Console.ReadLine());

        Console.WriteLine($"Squre of {num} is {num * num}");
    }
}

要在上面的示例中处理可能的异常,请将代码包装在一个try块中,然后在catch块中处理异常,如下所示。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("Enter a number: ");

            var num = int.parse(Console.ReadLine());

            Console.WriteLine($"Squre of {num} is {num * num}");
        }
        catch
        {
            Console.Write("Error occurred.");
        }
        finally
        {
            Console.Write("Re-try with a different number.");
        }
    }
}

在上面的示例中,我们将此代码包装在一个try块中。如果try块内发生异常,则程序将跳转到该catch块。在catch块内,我们将显示一条消息以指示用户有关其错误的信息,在finally块内,我们将显示一条有关运行程序后的操作的消息。

try块必须跟catch或finally或两种嵌段。在try不使用块catch或finally块将给出一个编译时错误。

理想情况下,catch块应包含内置或自定义异常类的参数以获取错误详细信息。以下包括Exception捕获所有类型的异常的type参数。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("Enter a number: ");

            var num = int.parse(Console.ReadLine());

            Console.WriteLine($"Squre of {num} is {num * num}");
        }
        catch(Exception ex)
        {
            Console.Write("Error info:" + ex.Message);
        }
        finally
        {
            Console.Write("Re-try with a different number.");
        }
    }
}

异常过滤器

您可以使用catch具有不同异常类型参数的多个块。这称为异常过滤器。当您想以不同的方式处理不同类型的异常时,异常过滤器很有用。

class Program
{
    static void Main(string[] args)
    {
        Console.Write("Please enter a number to divide 100: ");
        
        try
        {
            int num = int.Parse(Console.ReadLine());

            int result = 100 / num;

            Console.WriteLine("100 / {0} = {1}", num, result);
        }
        catch(DivideByZeroException ex)
        {
            Console.Write("不能被零除。请再试一次.");
        }
        catch(InvalidOperationException ex)
        {
            Console.Write("无效操作。请再试一次.");
        }
        catch(FormatException  ex)
        {
            Console.Write("不是有效格式。 请再试一次.");
        }
        catch(Exception  ex)
        {
            Console.Write("发生错误!请再试一次.");
        }
    }

}

在上面的示例中,我们指定了catch具有不同异常类型的多个块。我们可以根据错误向用户显示适当的消息,因此用户不会再次重复相同的错误。

注意:
不允许具有相同异常类型的多个catch块。具有基异常类型的catch块必须是最后一个块。

无效的catch块

在同一 try..catch语句中不允许使用无参数catch块和带有Exception参数的catch块,因为它们都执行相同的操作。

try
{
    //可能引发异常的代码
}
catch //不能同时具有catch和catch(Exception 异常)
{ 
    Console.WriteLine("Exception occurred");
}
catch(Exception ex) //不能同时具有catch和catch(异常异常)
{
    Console.WriteLine("Exception occurred");
}

另外,无参数 catch 块 catch {}或通用 catch 块 catch (Exception ex){}必须是最后一个块。如果在 catch {}或 catch (Exception ex)块之后还有其他 catch 块,编译器将给出错误。

    示例:无效的catch 捕获

try
{
    //可能引发异常的代码
}
catch
{ 
    // 该捕获块必须是最后一个块
}
catch (NullReferenceException nullEx)
{
    Console.WriteLine(nullEx.Message);
}
catch (InvalidCastException inEx)
{
    Console.WriteLine(inEx.Message);
}

finally 块

finally块是可选块,应在try或catch块之后。无论是否发生异常,都将始终执行 finally 块。finally块通常用于清理代码,例如处理非托管对象。

    示例:finally块

static void Main(string[] args)
{
    FileInfo file = null;

    try
    {
        Console.Write("Enter a file name to write: ");
        string fileName = Console.ReadLine();
        file = new FileInfo(fileName);
        file.AppendText("Hello World!")
    }
    catch(Exception ex)
    {
        Console.WriteLine("Error occurred: {0}", ex.Message );
    }
    finally
    {
        // 在这里清理文件对象;
        file = null;
    }
}
finally不允许使用 多个块。另外,finally块不能包含return,continue或break关键字。它不允许控制权离开finally块。

嵌套try-catch

C#允许嵌套try-catch块。当使用嵌套的try-catch块时,将在发生异常catch的try块之后的第一个匹配块中捕获异常。

static void Main(string[] args)
{
    var divider = 0;

    try
    {
        try
        {
            var result = 100/divider;
        }
        catch
        {
            Console.WriteLine("Inner catch");
        }
    }
    catch
    {
        Console.WriteLine("Outer catch");
    }
}
输出:
Inner catch

catch在上面的示例中,将执行一个内部块,因为它是catch处理所有异常类型的第一个块。

如果没有与抛出的异常类型匹配的内部catch块,则控制将流向外部catch块,直到找到合适的异常筛选器。看下面的实例。

static void Main(string[] args)
{
    var divider = 0;

    try
    {
        try
        {
            var result = 100/divider;
        }
        catch(NullReferenceException ex)
        {
            Console.WriteLine("Inner catch");
        }
    }
    catch
    {
        Console.WriteLine("Outer catch");
    }
}
输出:
Outer catch

在上面的示例中,将引发类型异常 DivideByZeroException 。由于内部catch块仅处理NullReferenceTypeException,因此将由外部catch块处理。