处理模式中垃圾收集文件类型的正确位置

Right place to garbage collect File type in dispose pattern

现在我的同事之间就需要在何处处理 C# 文件类型争论不休。 (https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose)

查看下面的代码,目前 File.Delete 在 if(disposing) 部分中,它被认为是托管对象。有些人认为它需要在循环之外并且需要放置在非托管对象中。

~someService() => Dispose(false);

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
    GC.Collect();
}

bool disposed = false;
protected virtual void Dispose(bool disposing)
{
    if (disposed)
    {
        return;
    }

    if (disposing)
    {
        // Dispose managed state (managed objects).
        tempFilePaths?.ToList().ForEach(f =>
        {
            if (File.Exists(f))
            {
                File.Delete(f);
            }
        });

        this._engine?.Dispose();
    }

    // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
    // TODO: set large fields to null.
    this._fields = null;

    disposed = true;
}

请仔细阅读 IDisposable 模式以了解这些 "sections" 的含义以及它们将在何时执行。

TL;DR
您不使用 anyif (disposing) 块之外的托管对象的现有引用。在您的示例中,tempFilePaths 似乎是 IEnumerable<string> 类型的 field/property,而 托管对象(现有引用)。因此,access/use 唯一正确的位置是在 if (disposing) 块内。

原因
if (disposing) 之外的代码在调用 public Dispose() 方法和被终结器调用时运行 both

终结器将在特殊的终结器线程上调用。不能保证 - 在终结器调用时 - tempFilePaths 将完全可用。尽管对象引用由您的 class 实例持有,但它可能已经被 GC 收集了。这就是终结器的工作方式。

如果您想在与您的团队讨论时获得更多背景知识和更多论据,请仔细阅读 Eric Lippert(前 C# 编译器团队成员)关于终结器的这些博文:

When everything you know is wrong, part one
When everything you know is wrong, part two