在 try 块中返回的伪代码是什么?

下面的代码


class Animal : IDisposable

{

    static string Invoker()

    {

        using (Animal a = new Animal())

        {

            return a.Greeting();

        }

    }


    public void Dispose()

    {

        Console.WriteLine("Disposed");

    }


    public string Greeting()

    {

        return "Hello World";

    }


    static void Main()

    {

        Console.WriteLine("Before");

        Console.WriteLine(Invoker());

        Console.WriteLine("After");

    }

}

产生


Before

Disposed

Hello World

After

因为这个输出中 Disposed 出现在 Hello World 之前,我猜


using (Animal a = new Animal())

{

    return a.Greeting();

}

相当于


Animal a = new Animal();

string buffer = null;

try

{

    buffer = a.Greeting();

}

finally

{

    a.Dispose();

    return buffer;

}

但是,它显然不正确,因为出现以下错误消息:


控制不能离开finally块的主体。


那么我最终的猜测就变成了如下。


Animal a = new Animal();

string buffer = null;

try

{

    buffer = a.Greeting();

    return buffer;

}

finally

{

    a.Dispose();

    //return buffer;

}

但是,我仍然很困惑,如果 在执行顺序中排在第一位,如何调用 Dispose() 。 提前离开函数体使得 必须由某些东西(其他代理或线程或垃圾收集器或我不详细了解的任何隐藏机制)调用。这是我的想象。 另外,如果 首先出现,那么输出应该是return bufferDispose()return buffer


Before

Hello World

Disposed

After

问题

你能告诉我编译器或内部机制如何调用Dispose() while return buffer 首先出现,导致过早离开函数体Invoker?


慕森王
浏览 125回答 2
2回答

茅侃侃

语言规范 指出这种形式的 using 语句:using (ResourceType resource = expression) statement相当于:{    ResourceType resource = expression;    try {        statement;    }    finally {        ((IDisposable)resource).Dispose();    }}因此,您的 using 语句相当于:{    Animal a = new Animal();    try {        return a.Greeting();    } finally {        a.Dispose();    }}我只能猜测为什么你认为这是违反直觉的。也许是因为您认为由于 而无法到达 finally?那么,规范还指定:returnfinally 块中的语句总是在控制时执行 留下 try 语句。无论控制权转移是否如此 发生作为正常执行的结果,作为执行的结果 break、continue、goto 或 return 语句,或作为以下结果 从 try 语句中传播异常。

临摹微笑

该字符串在处理后返回,然后写入控制台。该字符串在方法结束时返回,即在 dispose 之后返回。“返回”是指“返回”。函数完成所有操作后返回一个变量。这就好比一个 goto 是通过所有的finally块到达方法的末尾,然后只在这里返回值。在您的情况下,您的代码相当于:static string Invoker(){    string result;    using (Animal a = new Animal())    {        result = a.Greeting();        goto end;        // a return here is like a "goto end"        // done after invoking the Dispose()        // while exiting the using block    }    // others things possible here    // return anything_else_here;    end:      return result;}下面是 VS2017 生成的 IL 代码(使用 .NET Reflector):.method private hidebysig static string Invoker() cil managed{    .maxstack 1    .locals init (        [0] class ConsoleApp1.Animal a,        [1] string str)    L_0000: nop     L_0001: newobj instance void ConsoleApp1.Animal::.ctor()    L_0006: stloc.0     L_0007: nop     L_0008: ldloc.0     L_0009: callvirt instance string ConsoleApp1.Animal::Greeting()    L_000e: stloc.1     L_000f: leave.s L_001c    L_0011: ldloc.0     L_0012: brfalse.s L_001b    L_0014: ldloc.0     L_0015: callvirt instance void [mscorlib]System.IDisposable::Dispose()    L_001a: nop     L_001b: endfinally     L_001c: ldloc.1     L_001d: ret     .try L_0007 to L_0011 finally handler L_0011 to L_001c}如您所见,ret 位于调用 dispose 之后的末尾。具体来说,代码将字符串压入堆栈,并在返回到调用方法后将其弹出堆栈以检索字符串。在此示例中,控制台写入两次内容并等待按键,然后退出该方法:static string Test(){  try  {    try    {      return "a string"; // the string is pushed in the stack here    }    finally    {      Console.WriteLine("1");      Console.ReadKey();      // no other return allowed here    }  }  finally  {    Console.WriteLine("2");    Console.ReadKey();    // no other return allowed here  }}// The method that calls Test() next pop the stack to retreive the string这与在该块末尾调用 Dispose 的 using 块发生的情况相同,而要返回的值位于等待调用者弹出的堆栈中。goto的解释是一个晦涩的解释,如果你了解IL的话你可能会忘记它,但它可以帮助你理解。
打开App,查看更多内容
随时随地看视频慕课网APP