为什么 using 变量在离开方法之前自行处理?

Why is the using variable disposing itself before leaving the method?

using PuppeteerSharp;
using System;
using System.Threading.Tasks;

namespace Video_Handler
{
    public class VideoHost
    {
        private static bool Is_HTTPS_URL(string url)
        {
            Uri uriResult;

            bool result = Uri.TryCreate(url, UriKind.Absolute, out uriResult) && (uriResult.Scheme == Uri.UriSchemeHttps);
            return result;
        }

        #region BrowserCreationCode
        private static async Task FectchBrowserIfItIsNotThere()
        {
            var browserFetcher = new BrowserFetcher();
            await browserFetcher.DownloadAsync();
        }
        internal static async Task<Page> GetPageAsync(string url, bool isPageInvisible = true, Action<Page> action)
        {
            if(!Is_HTTPS_URL(url))
            {
                throw new ArgumentException($"The url | {url} | is not an https url.");
            }
            await FectchBrowserIfItIsNotThere();
            await using var browser = await Puppeteer.LaunchAsync(
                new LaunchOptions { Headless = isPageInvisible });
            await using var page = await browser.NewPageAsync();
            await page.GoToAsync(url);
            action(page);
            return page;
        }
    }
}
//Boiler Plate code
async static void Main()
{
  Action<Page> action = (page) => Foo(page);
  await GetPageAsync("https://google.com", false , action);
}
public static async Task Foo(Page p){ 
   await p.EvaluateExpression("//fill google search bar"); //first line
   await p.EvaluateExpression("//click search button");  //second line
   await p.WaitForNavigationAsync(); //third line
   await p.EvaluateExpression("alert("hi")"); //fourth line
}

我遇到一个问题,当我将 page 变量发送到接受页面变量的操作时,它导致页面或浏览器变量被释放。根据我对使用的理解,只有在我离开函数的括号时才会发生这种情况。我已经阅读了微软关于 using 的文档,但我找不到任何告诉我页面变量在去using 变量所在函数中的另一个函数。

此外,有时代码能够计算函数的某些表达式,但通常会在第三个时失败。但是,如果我一步步调试 运行 ,它总是在第一行失败。这就是让我相信这是一个处理问题的原因。

对于任何想知道为什么我不直接从 browserpage 变量中删除 using 并自行处理它们的人,我有两个原因。第一,我没有处置非托管代码的经验,当我尝试将处置添加到 finalizer/destructor 时它不起作用,其次,我不想编写代码来完成该语言已经完成的工作对我来说。

问题是使用 Action 而不是 Func

行动总是returns无效。由于调用的函数 Action 是一个异步方法,但没有发送该方法的输出,因此导致调用 Action 的函数退出,从而释放了未管理的资源。 Func 保留输出,因此可以等待它,这样主线程就会被阻塞直到完成,从而防止非托管资源被提前释放。

async static void Main()
{
   Func<FileStream,Task> func = (FileStream fs) => Foo(fs); //Input to function is FileStream and Output of Function is Task
   Action<FileStream> action = (FileStream fs) => Foo(fs); //Equivalent to Func<FileStream , void>
   using var fs = new FileStream();
   //action(fs); //This can fail if the main thread is finished before it
   await func(fs); //This will succeed since the main thread is blocked till completion

}
async static Task Foo(FileStream fs) => await fs.SomeLongAsyncMethod();