为什么在 C# 中使用一次性 pastern 时需要创建一个 属性 来检查资源是否已被释放?

Why do I need to create a property to check if a resource has been disposed when using disposable pastern in c#?

我需要编写一个 class,我想让消费者能够通过在 C# 中用 using(...) 语句包装代码来处理代码。

为此,我必须实现 Microsoft 的 IDisposable 接口。

基于Microsoft approach on implementing it,我应该这样做

一个通用接口

public interface ISomeClass : IDisposable
{
     // ... some methods to include
}

public class SomeClass : ISomeClass
{
     private readonly TimeTrackerContext _context;

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed && disposing && _context != null)
        {
            _context.Dispose();

        }
        this.disposed = true;
    }

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

我正在尝试以正确的方式学习 C#,所以我对这个实现有一些疑问。

问题

为什么我真的需要一个 属性 来告诉我对象是否已被处置,然后再处置它?

换句话说,我不能在处理之前检查 _context 是否为 null 吗?像这样

public class SomeClass : ISomeClass
{
    private readonly TimeTrackerContext _context;

    private void SelfDisposing()
    {
        if (_context != null)
        {
            _context.Dispose();
        }

    }

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

    private void SelfDisposing(bool disposing)
    {
        if (_context != null && !this.disposed && disposing)
        {
            _context.Dispose();
        }

        this.disposed = true;
    }
}

_context 不会 null 如果对象已经被释放。它仍然会引用已处置的对象。

现在,如果您无需存储布尔变量就可以确定您的对象是否已被释放,那很好。

不应该实际拥有的是GC.SuppressFinalize你的对象没有终结器,所以没有什么可以抑制的。

指示Dispose已经被调用的字段(或属性)对于实现Dispose(bool)方法本身不是强烈要求的,如以下规则所述:

✓ DO allow the Dispose(bool) method to be called more than once. The method might choose to do nothing after the first call.

如果您有其他方法/属性并且希望从同一文档中实施以下规则,则需要它:

✓ DO throw an ObjectDisposedException from any member that cannot be used after the object has been disposed of.

我想分享一下我对处置模式的看法。根据我的经验,您需要在 class 包含非托管资源或 class 被设计为继承的情况下使用该模式(即使它没有非托管资源只是为了提供对于将实现派生 classes 的程序员来说,这是众所周知的逻辑)。在大多数情况下,您似乎编写了一堆无用的代码。所以在大多数情况下你需要做的是:

public sealed class SomeClass : ISomeClass
{
    private readonly TimeTrackerContext _context;

    public void Dispose()
    {
        _context.Dispose();
    }
}

第二个想法是处理 _context 字段本身。如果您通过构造函数传递上下文,您实际上并不知道是否真的需要处理它。 .NET Stream-based classes(如 StreamWriter)在 .NET 4.0 之前也一直存在这个问题。解决方案是编写 Stream 包装器(如 NonDisposableStreamWrapper),它在调用 dispose 时不处理内部流。真正解决这个问题的方法是在构造函数中添加额外的 dispose 字段,指定是否释放内部 class 。例如:

public sealed class SomeClass : ISomeClass
{
    private readonly TimeTrackerContext _context;
    private bool _dispose;

    public SomeClass(TimeTrackerContext context, bool dispose = true)
    {
        _context = context;
        _dispose = dispose;
    }

    public void Dispose()
    {
        if (_dispose)
        {
            _context.Dispose();
        }
    }
}