为什么我的 C# finally 块不起作用?

6

我一直在帮助同事调试他们代码中的一些奇怪行为。以下示例说明了这一点:

static void Main(string[] args)
{
    string answer = Sample();
    Console.WriteLine(answer);
}

public static string Sample()
{
    string returnValue = "abc";

    try 
    {
         return returnValue;
    }

    catch (Exception)
    {
         throw;
    }

    finally
    {
         returnValue = "def";
    }
}

这个示例代码会返回什么?
你可能会认为由于 finally 代码块的存在,它将返回 "def",但实际上它返回的是 "abc"?我已经逐步执行了代码并确认 finally 代码块确实被调用了。
真正的答案是你不应该一开始就编写这样的代码,但我仍然对其行为感到困惑。
编辑:为了澄清一些答案基础上的流程。
当您逐步运行代码时,在 return 之前会执行 finally 块。
重复:在 try { return x; } finally { x = null; } 语句中到底发生了什么?
6个回答

12
你的 "finally" 块给 returnValue 赋值而不是实际返回一个值。在 finally 块改变值之前已经发生了 "return",因此返回的是 "abc"。
虽然代码很令人困惑,因为你所做的事情没有意义,但它所做的是正确的。

如果return语句位于try块内部,则在控制返回到调用方法之前,finally块(如果存在)将被执行。 - TStamper
TStamper - 在try中计算返回值时,变量仍然是"abc",然后执行finally并更改变量赋值,但不会改变已缓存的返回值。 - user1921
翻译:true,但措辞不正确。从他所说的内容来看,在Sample函数中代码并没有继续运行。 - TStamper

3

是的,finally块在函数返回后运行,但这并不重要。请记住,返回值按值传递,因此在返回时会为其创建一个新的临时变量,因此finally块不会影响实际的返回值。如果您想支持所需的行为,则可以使用out参数,如下所示:

static void Main(string[] args)
{
    string answer;
    Sample(out answer);
    Console.WriteLine(answer);
}

public static void Sample(out string answer)
{

    try
    {
        answer = "abc";
        return;
    }

    catch (Exception)
    {
        throw;
    }

    finally
    {
        answer = "def";
    }
}

或者,您可以将返回语句放在try块之外,像这样:

static void Main(string[] args)
{
    string answer = Sample();
    Console.WriteLine(answer);
}

public static string Sample()
{
    string returnValue;
    try
    {
        returnValue = "abc";
    }

    catch (Exception)
    {
        throw;
    }

    finally
    {
        returnValue = "def";
    }

    return returnValue;
}

然而,考虑到finally块将始终覆盖返回值,这是一个值得怀疑的设计。


3
< p > finally 块会在 return 语句之后执行。因此,在进入 finally 块之前,您已经返回了旧值 abc

(这不是底层实现的确切方式,但足以说明问题)


0

我不是专家,但我猜测这个函数会先返回,然后再调用 finally 块。由于 return returnValue 已经被执行,returnValue 在 finally 块中的取值并不重要。这种行为有点合理,因为它应该在执行整个 try 块之前执行 finally 块,而它能够做到这一点的唯一方法就是按照预期从函数中返回。


0

如果你真的很想知道正在发生什么,那么你可以下载并安装 Reflector。这是一个非常好的工具,可以放入你的“技巧袋”中。它将告诉你引擎盖下面正在发生什么。


0

我猜你正在确定在return语句处返回什么(一个指向字符串“abc”的引用)。

因此,finally后面将该引用设置为引用不同的字符串对返回值没有影响。


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