Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# GC.KeepAlive以保留上下文_C#_.net_Garbage Collection_Using_Idisposable - Fatal编程技术网

C# GC.KeepAlive以保留上下文

C# GC.KeepAlive以保留上下文,c#,.net,garbage-collection,using,idisposable,C#,.net,Garbage Collection,Using,Idisposable,我有一个类,它是WNetUseConnection 以下是一个实现(仅供参考): 下面是用法: using (var context = WindowsNetworking.CreateRemoteContext(storagePath, login, pass)) { // do something with storagePath GC.KeepAlive(context); } 问题是我是否应该编写GC.KeepAlive(context)?我的意思是直到我读了一篇文章(

我有一个类,它是
WNetUseConnection

以下是一个实现(仅供参考):

下面是用法:

using (var context = WindowsNetworking.CreateRemoteContext(storagePath, login, pass))
{
    // do something with storagePath
    GC.KeepAlive(context);
}
问题是我是否应该编写
GC.KeepAlive(context)
?我的意思是直到我读了一篇文章(关于
AsyncLock
),我才写这样的代码,但现在我找不到链接,现在我不确定GC是否可以在这个方法完成之前调用终结器。理论上,它应该在
使用
最后
部分中使用
处置
,但是这篇文章是由一个聪明人写的,所以我现在不确定


以防万一,我为引用的类提供代码:

public static class WindowsNetworking
{
    public static bool TryConnectToRemote(string remoteUnc, string username, string password, bool promptUser = false)
    {
        bool isUnc = remoteUnc != null && remoteUnc.Length >= 2 && remoteUnc[0] == '\\' && remoteUnc[1] == '\\';
        if (!isUnc)
        {
            return false;
        }
        ConnectToRemote(remoteUnc, username, password, promptUser);
        return true;
    }

    public static IDisposable CreateRemoteContext(string remoteUnc, string username, string password, bool promptUser = false)
    {
        return new RemoteFileSystemContext(remoteUnc, username, password, promptUser);
    }

    public static void DisconnectRemote(string remoteUNC)
    {
        var ret = (NetworkError) WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
        if (ret != NetworkError.NO_ERROR)
        {
            throw new Win32Exception((int) ret, ret.ToString());
        }
    }

    [DllImport("Mpr.dll")]
    private static extern int WNetUseConnection(
        IntPtr hwndOwner,
        NETRESOURCE lpNetResource,
        string lpPassword,
        string lpUserID,
        int dwFlags,
        string lpAccessName,
        string lpBufferSize,
        string lpResult
        );

    [DllImport("Mpr.dll")]
    private static extern int WNetCancelConnection2(
        string lpName,
        int dwFlags,
        bool fForce
        );

    [StructLayout(LayoutKind.Sequential)]
    private class NETRESOURCE
    {
        public int dwScope = 0;
        public int dwType = 0;
        public int dwDisplayType = 0;
        public int dwUsage = 0;
        public string lpLocalName = "";
        public string lpRemoteName = "";
        public string lpComment = "";
        public string lpProvider = "";
    }

    private static void ConnectToRemote(string remoteUNC, string username, string password, bool promptUser)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            dwType = RESOURCETYPE_DISK,
            lpRemoteName = remoteUNC
        };

        NetworkError ret;
        if (promptUser)
            ret = (NetworkError) WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
        else
            ret = (NetworkError) WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);

        if (ret != NetworkError.NO_ERROR)
        {
            throw new Win32Exception((int) ret, ret.ToString());
        }
    }
}

测试非常容易,这里有一个快速测试程序,确保它在没有附加调试程序的情况下以发布模式运行

using System;

namespace SandboxConsole
{ 
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new TestClass())
            {

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();

                Console.WriteLine("After collection");
            }
            Console.WriteLine("After dispose, before 2nd collection");

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Console.WriteLine("After 2nd collection");
            Console.ReadLine();
        }
    }

    internal class TestClass : IDisposable
    {
        public void Dispose()
        {
            Dispose(true);
        }

        ~TestClass()
        {
            Console.WriteLine("In finalizer");
            Dispose(false);
        }

        private void Dispose(bool isDisposing)
        {
            Console.WriteLine("In Dispose: {0}", isDisposing);
            if (isDisposing)
            {
                //uncomment this line out to have the finalizer never run
                //GC.SuppressFinalize(this);
            }
        }
    }
}
它将始终输出

After collection In Dispose: True After dispose, before 2nd collection In finalizer In Dispose: False After 2nd collection 您可以看到有一个隐藏的finally块,用于检查对象是否为null,然后对其调用Dispose。该引用将使对象在使用块的整个范围内保持活动状态


更新:请参阅,这个特定示例确实有机会提前调用终结器,因为我从未在dispose方法中使用任何使用隐式
this
的变量。为了保证行为,请确保使用实例级变量(我的简短示例中没有)或使用
GC.SuppressFinalize(此)
未注释。

GC.KeepAlive方法是。它所做的只是确保在代码中的该点读取特定变量,否则该变量将不会再次读取,因此不是保持对象活动的有效引用


这里没有意义,因为您传递给
KeepAlive
的同一变量将在稍后的时间点再次读取-在调用
Dispose
时的隐藏
finally
块期间。因此,
GC.KeepAlive
在这里一事无成。

我会说不。没有这篇文章,很难说这个“聪明人”到底提出了什么观点,以及你是否正确地解释了它。这只是
从哪里来的历史。问题不是关于任何人,而是关于定稿。我可以说,“你不需要一个
GC.KeepAlive
在这里”。我甚至可以把它贴出来作为一个答案,我自己也是一个相当聪明的人(而且也很谦虚!),我甚至可能是令人信服的。问题是,我不知道我反对的是什么观点,因为你不能链接到这篇文章,也不能说为什么你认为它可能适用于这种情况。如果你真的使用了WNetUseConnection(),这段代码的作用是很难猜测的。当你pinvoke任何东西时,没有明显的理由使用GC.KeepAlive()。@HansPassant感谢你的评论,
TryConnectToRemote
实际上调用了
WNetUseConnection
如果提供的路径是UNC,否则它什么也不做。我只是确信它与问题不太相关。一个聪明的GC/JIT组合实际上可能比显式的
Dispose
更早完成这个对象-如果它可以跨多个方法进行优化,
TestClass
中没有引用该类的任何字段,因此任何方法都不需要
this
引用。与OPs类不同,OPs类访问
Dispose
中的本地字段。所以这是一个观察,而不是一个保证。@Damien_不信者真的,我不知道JIT能走那么远。谢谢你的警告。我不知道是否有人会这么做,只是这是一种可能性。@ScottChamberlain规范至少会允许它这样做。从技术上讲,如果
Dispose
不访问任何字段,或者使用隐式引用,这是不正确的(如果运行时可以证明
Dispose
不需要对对象的引用,那么它可以在执行该代码之前回收该对象),但如果它引用了对象,那么这是完全正确的。如果
Dispose
实际上没有利用对象实例,那么,从GC的目的来看,它不会延长对象的生命周期;
GC.KeepAlive
将。因此在这一特定情况下,它是相关的。这是使用
GC.suppressFinalize的原因之一e(这)
Dispose
中,即使在没有终结器的对象上,也要确保任何具有终结器的嵌套对象在
Dispose
完成之前保持活动状态。 After collection In Dispose: True After dispose, before 2nd collection In finalizer In Dispose: False After 2nd collection
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       85 (0x55)
  .maxstack  1
  .locals init ([0] class SandboxConsole.TestClass context)
  IL_0000:  newobj     instance void SandboxConsole.TestClass::.ctor()
  IL_0005:  stloc.0
  .try
  {
    IL_0006:  call       void [mscorlib]System.GC::Collect()
    IL_000b:  call       void [mscorlib]System.GC::WaitForPendingFinalizers()
    IL_0010:  call       void [mscorlib]System.GC::Collect()
    IL_0015:  ldstr      "After collection"
    IL_001a:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_001f:  leave.s    IL_002b
  }  // end .try
  finally
  {
    IL_0021:  ldloc.0
    IL_0022:  brfalse.s  IL_002a
    IL_0024:  ldloc.0
    IL_0025:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_002a:  endfinally
  }  // end handler
  IL_002b:  ldstr      "After dispose, before 2nd collection"
  IL_0030:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0035:  call       void [mscorlib]System.GC::Collect()
  IL_003a:  call       void [mscorlib]System.GC::WaitForPendingFinalizers()
  IL_003f:  call       void [mscorlib]System.GC::Collect()
  IL_0044:  ldstr      "After 2nd collection"
  IL_0049:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_004e:  call       string [mscorlib]System.Console::ReadLine()
  IL_0053:  pop
  IL_0054:  ret
} // end of method Program::Main