C#中的finally块

5

可能是重复问题:
finally块不执行??

我有一个关于C#中finally块的问题。 我写了一段简单的示例代码:

public class MyType
{
    public void foo()
    {
        try
        {
            Console.WriteLine("Throw NullReferenceException?");
            string s = Console.ReadLine();
            if (s == "Y")
                throw new NullReferenceException();
            else
                throw new ArgumentException();          
        }
        catch (NullReferenceException)
        {
            Console.WriteLine("NullReferenceException was caught!");
        }
        finally
        {
            Console.WriteLine("finally block");
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyType t = new MyType();
        t.foo();
    }
}

据我所知,finally块应该在抛出异常与否的情况下都能确定地运行。现在,如果用户输入“Y”-会抛出NullReferenceException异常,执行移动到catch块,然后像我预期的那样移动到finally块。但是,如果输入是其他内容,则会抛出ArgumentException异常。由于没有合适的catch块来捕获此异常,因此我认为执行应该移动到finally块-但实际上并没有。请有人可以解释一下原因吗?谢谢大家 :)

2
可能是重复问题:https://dev59.com/Vm855IYBdhLWcg3wsWr3 - Matt Ellen
2
我已经尝试了这段代码,它按预期进入了 finally 块。 - SWeko
你能澄清一下你所说的“我认为执行应该移动到finally块”是什么意思吗?我假设在两种情况下控制都会进入finally块,对吗? - Jagmag
是的,显然它确实进入了 finally 块,但由于调试器的缘故我错过了这一点... :| - meem
2个回答

6
你的调试器可能会捕捉到ArgumentException异常,因此在进入最终块之前等待你“处理”它。在不附加调试器(包括不附加JIT调试器)的情况下运行代码,它应该会触发你的finally块。
要禁用JIT,请转到“选项>工具>调试>即时(JIT)”并取消选中“托管代码”。
要在不附加调试器的情况下进行调试,在Visual Studio中转到“调试>开始调试(无调试)”(或按下CTRL + F5)。
在程序的结尾处放置一个Console.ReadLine()也会有帮助,以防止控制台在进入finally块后关闭。
class Program {
    static void Main(string[] args) {
        MyType t = new MyType();
        t.foo();
        Console.ReadLine();
    }
}

以下是您应该得到的输出:


抛出NullReferenceException?N

未处理的异常:System.ArgumentException:值不在预期范围内。

位于ConsoleSandbox.MyType.foo()中 P:\ Documents \ Sandbox \ Console \ Console \ Program.cs:第17行

在 ConsoleSandbox.Program.Main(String[] args)中P:\ Documents \ Sandbox \ Console \ Console \ Program.cs:第31行

最终块

按任意键继续...


好的,你说得对。我没有想到这与调试器有关,因为我在异常窗口中尝试了CLR运行时异常的“抛出”和“用户未处理”的4种组合。这不相关吗?我的意思是,如果我取消选中这些框,代码不应该受到任何干扰吧?再次感谢你的时间。 - meem
你指的是[调试>异常]选项;那与我上面提到的即时调试器选项是不同的。即时调试器会捕获未处理的异常,而你所提到的异常选项是Visual Studio附加的调试器。 - bitxwise
只是想提一下,Henk Holterman 也是正确的,将 t.foo() 调用包围在 try/catch 中将显示 finally 块确实被调用了 - 但这会改变您的情况,即您的 ArgumentException 成为已处理的异常,JIT 调试器不再让它看起来像您的 finally 块没有被调用。这就是为什么我们应该始终处理我们的异常 =) - bitxwise
我同意我的代码确实是一个不好的实践。它只是为了学习目的而编写的。 - meem

1

你看到的是测试程序的产物。

如果你改变了主方法:

 static void Main(string[] args)
 {
    try
    {
        MyType t = new MyType();
        t.foo();
    }
    catch
    {
       // write something
    }
 }

然后你的foo()将按预期运行。

如果没有顶层的try/catch,整个程序都会被中止。


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