C# - 异常处理

本文关键字:异常处理 | 更新日期: 2023-09-12 17:39:50

在这里,您将了解 C# 中使用 try、catch 和 finally 块进行异常处理。

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

语法:
try
{
    // put the code here that may raise exceptions
}
catch
{
    // handle exception here
}
finally
{
    // final cleanup code
}

try block:任何可能引发异常的可疑代码都应放在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块后面必须跟着catchfinally或两个块。没有 catchfinally 块的try块将给出编译时错误。

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

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("Cannot divide by zero. Please try again.");
        }
        catch(InvalidOperationException ex)
        {
            Console.Write("Invalid operation. Please try again.");
        }
        catch(FormatException  ex)
        {
            Console.Write("Not a valid format. Please try again.");
        }
        catch(Exception  ex)
        {
            Console.Write("Error occurred! Please try again.");
        }
    }
}

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

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

无效的捕获块

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

try
{
    //code that may raise an exception
}
catch //cannot have both catch and catch(Exception ex)
{ 
    Console.WriteLine("Exception occurred");
}
catch(Exception ex) //cannot have both catch and catch(Exception ex)
{
    Console.WriteLine("Exception occurred");
}

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

try
{
    //code that may raise an exception
}
catch
{ 
    // this catch block must be last block
}
catch (NullReferenceException nullEx)
{
    Console.WriteLine(nullEx.Message);
}
catch (InvalidCastException inEx)
{
    Console.WriteLine(inEx.Message);
}

finally Block

finally块是可选块,应位于try或捕获块之后。无论是否发生异常,都将始终执行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
    {
        // clean up file object here;
        file = null;
    }
}

注意: 不允许多个finally块。此外,finally块不能有返回、继续或中断关键字。它不会让控制权离开finally块。

嵌套尝试捕获

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

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块处理。