简体   繁体   中英

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

I need to be write a class where I want to enable the consumer to dispose the code by wrapping the code with using(...) statement in C#.

To do that, I must implement Microsoft's IDisposable interface.

Based on Microsoft approach on implementing it , I should do something like this

a generic interface that so

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);
    }
}

I am trying to learn C# the correct way so I have some question regarding this implementation.

Question

Why do I really need to have a property that tell me if the object has been disposed or not before I dispose it?

In other words, Can't I just check if the _context is null instead before disposing it? Something like this

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 won't be null if the object has already been disposed. It'll still reference the already disposed object.

Now if you can determine whether or not your object is disposed without needing to store a boolean variable, then that's fine.

What you shouldn't actually have is GC.SuppressFinalize Your object has no finalizer, so there is nothing to suppress.

The field (or property) indicating that the Dispose has been already called is not strongly required for implementing the Dispose(bool) method itself, as mentioned in the following rule:

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

It's needed if you have other methods / properties and you want to implement the following rule from the same document:

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

I would like to share my thoughts on dispose pattern. From my experience, you need to use the pattern in situation, when a class contains an unmanaged resource, or the class is designed to be inherited (even if it does not have an unmanaged resource just to provide well-known logic for the programmers who will implement derived classes). In most cases it seems like you write a bunch of useless code. So in most cases what you need to do is:

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

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

The second thought is disposing the _context field itself. In case if you pass context via constructor you actually do not know if you really need to dispose it. .NET Stream-based classes (like StreamWriter) also have been suffering from this problem till .NET 4.0. And the solution was to write Stream wrappers (like NonDisposableStreamWrapper) which do not dispose internal stream when dispose is called. The really good solution to this problem is to add additional dispose field in the constructor, which specifies whether to dispose the internal class or not. For example:

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();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM