为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
慕森王 2023-12-17 10:03:52
下面的代码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");    }}产生BeforeDisposedHello WorldAfter因为这个输出中 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 bufferBeforeHello WorldDisposedAfter问题你能告诉我编译器或内部机制如何调用Dispose() while return buffer 首先出现,导致过早离开函数体Invoker?
查看完整描述

2 回答

?
茅侃侃

TA贡献1842条经验 获得超21个赞

语言规范 指出这种形式的 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?那么,规范还指定:return

finally 块中的语句总是在控制时执行 留下 try 语句。无论控制权转移是否如此 发生作为正常执行的结果,作为执行的结果 breakcontinuegoto 或 return 语句,或作为以下结果 从 try 语句中传播异常。


查看完整回答
反对 回复 2023-12-17
?
临摹微笑

TA贡献1982条经验 获得超2个赞

该字符串在处理后返回,然后写入控制台。


该字符串在方法结束时返回,即在 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的话你可能会忘记它,但它可以帮助你理解。


查看完整回答
反对 回复 2023-12-17
  • 2 回答
  • 0 关注
  • 63 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信