3

If I have a class which makes use of managed resources only, so I don't see any need to fully implement the IDisposable pattern.

Surely this is sufficient:

    public class ManagedResourceClient : IDisposable
    {
        private ITheManagedResource _myManagedResource = new TheManagedResource()

        public void Dispose()
        {
            if ( _myManagedResource != null )
            {
                _myManagedResource.Dispose();
                _myManagedResource = null;
            }
        } 
    }

I don't see any reason to use:

  • a finalizer as this is using only managed resources that implement IDisposable
  • a 'disposing' flag as this is handled by a null check
  • a virtual Disposing method as there is no need to differentiate between GC calls and direct calls.

Can the above be confirmed as correct?

user183872
  • 767
  • 2
  • 6
  • 20
  • 1
    Is this application multithreaded? – Marshall Tigerus Nov 15 '16 at 15:38
  • 3
    "I have a class which makes use of managed resources only" Then you probably don't need dispose – Steve Nov 15 '16 at 15:39
  • @Steve Not true at all, as is seen right in the example code, where the object references other managed disposable objects. For almost every C# programmer in the world, this is the only type of `IDisposable` class they'll ever need to write. – Servy Nov 15 '16 at 15:40
  • 1
    @Servy: voila, the next object that implements `IDisposable` for no reason – Tim Schmelter Nov 15 '16 at 15:41
  • @Servy that other object ref is named ManagedResource. I would assume that one doesn't need dispose either – Steve Nov 15 '16 at 15:41
  • @Steve Considering that he calls `Dispose` on it, clearly that's not the case. – Servy Nov 15 '16 at 15:42
  • @TimSchmelter What basis do you have for that assertion? Perhaps it wraps an unmanged resource, or perhaps it wraps another managed resource that itself has an unmanaged resource. – Servy Nov 15 '16 at 15:42
  • 1
    Saying you have a class inhereting from ManagedResourceClient and it uses many unmanaged resources. You re-implement the IDisposable again !! Here the use of your virtual Dispose(bool) method – Med.Amine.Touil Nov 15 '16 at 15:42
  • @Servy: he seems to think that every class needs to implement `IDisposable` – Tim Schmelter Nov 15 '16 at 15:43
  • 1
    OP you need to mention whether you are working with unmanaged resource. If the whole application is working with Managed resource only then no you don't need it – Steve Nov 15 '16 at 15:43
  • @TimSchmelter I guess so !! – Med.Amine.Touil Nov 15 '16 at 15:44
  • @TimSchmelter What's your basis for saying that? He's asking how to properly implement `IDisposable` for a class that has fields that are themselves `IDisposable`. Nowhere does he say, or indicate, that every class should be disposable. – Servy Nov 15 '16 at 15:44
  • @Servy: my basis is what he has shown. He even uses the name `TheManagedResource` which clearly indicates that it only contains managed resources. But it still implements `IDisposable`. Following this pattern it's no wonder that he needs to implement it here too. But the question is not how but why. There's no need to implement it here if `TheManagedResource` doesn't implement it. – Tim Schmelter Nov 15 '16 at 15:46
  • @TimSchmelter So you think it's never appropriate to have a class with a field that is a managed object that implements `IDisposable`? So you've never ever written a class with a `Stream` as a field, for example, even though `Stream` is a managed resource that implements `IDisposable`? Saying that it's a managed resource simply means that it is itself a managed wrapper around an unmanaged resource, rather than an actual unmanaged resource itself, which is of course relevant to the question; if the field was actually, say, an unsafe file pointer, then he couldn't write this code. – Servy Nov 15 '16 at 15:48
  • @Servy Sorry will clarify the example. The managed resource in question is still IDisposable otherwise the Dispose() call would not compile. – user183872 Nov 15 '16 at 15:50
  • @Servy: A stream is a managed resource? From [MSDN](https://msdn.microsoft.com/en-us/library/system.io.stream(v=vs.110).aspx): _"... Dispose also releases operating system resources such as file handles, network connections, or memory used for any internal buffering. ..."_ – Tim Schmelter Nov 15 '16 at 15:51
  • @TimSchmelter Yes, a `Stream` is a managed resource. It *composes* unmanaged resources. The whole purpose of the class is to be a *managed* wrapper around the unmanaged resources so that consumers of the class don't actually need to concern themselves with the details of the unmanaged resources, and can instead deal entirely with a *managed* resource. – Servy Nov 15 '16 at 15:53
  • @MarshallTigerus no this is not multi-threaded. I'm guessing that would change things, but I want to keep this example as simple as possible. – user183872 Nov 15 '16 at 15:54
  • Really reccomended reading for this topic by Stepen Cleary: "[IDisposable: What Your Mother Never Told You About Resource Deallocation](http://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About)" – Scott Chamberlain Nov 15 '16 at 15:54
  • @Servy What I understand from your comments, if you use a Stream as a field or a property in your class, this class doesn't implement IDsposable ? – Med.Amine.Touil Nov 15 '16 at 15:54
  • @Med.Amine.Touil I literally said the opposite. – Servy Nov 15 '16 at 15:55
  • @Servy .Sorry about my missunderstanding ! So we are in the same team now – Med.Amine.Touil Nov 15 '16 at 15:57
  • There is a theory that a Sqlcommand object is something not requiring disposal, and therefore should not be disposed - a theory I don't subscribe to personally – Cato Nov 15 '16 at 16:32
  • @Cato The only reason `SqlCommand` is disposeable is because it inherits from `Component` and `Component` is disposable. The only thing it actually does in it's dispose method is [set a private variable that represents a metadata cache to be null](https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,53ad9885e5a8fc48). It is just like `DataTable`, disposable class, but no one ever disposes it. – Scott Chamberlain Nov 15 '16 at 16:48

1 Answers1

5

You almost had it, because your class is not sealed someone could derive from your class and that derived class could also need to dispose a object. Make your class sealed and your current implementation is fine.

public sealed class ManagedResourceClient : IDisposable
{
    private ITheManagedResource _myManagedResource = new TheManagedResource()

    public void Dispose()
    {
        if ( _myManagedResource != null )
        {
            _myManagedResource.Dispose();
            _myManagedResource = null;
        }
    } 
}

If you want to learn more about disposing (and why the stock example with a finializer Microsoft gives is actually a bad example) see this really good article by Stepen Cleary: "IDisposable: What Your Mother Never Told You About Resource Deallocation"

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Ok thanks, and if it wasn't sealed what would I need to do? – user183872 Nov 15 '16 at 15:58
  • @user183872 make Dispose virtual or make a protected Dispose function and make that virtual (the making a protected function one is the "standard" dispose pattern) – Scott Chamberlain Nov 15 '16 at 15:59
  • You should make sure to read the article too, basically it goes over why you should never need to have a finializer in your class unless your class derives from `SafeHandle`. You seperate your logic in to two types, one that only deals with a single unmanaged object (Type 0) and one that only holds Type 0 or other Type 1 objects (Type 1). – Scott Chamberlain Nov 15 '16 at 16:03
  • @Scott - does it actually happen in reality that the Dispose pattern goes wrong? (Other than .dispose failing to be called by the programmer) - is this stuff in the article a real world problem or theoretical? – Cato Nov 15 '16 at 16:41
  • @Cato The main real-world situation you will see this be a factor is If you are generating lots of short lived disposable objects, by having a finializer on your disposable object your object can never be collected as part of the Gen 0 garbage collection because having a finalizer causes you to get promoted a teir when you get added to the finialzer queue. This can cause noticeable performance issues when you have a lot of very short lived disposable objects. – Scott Chamberlain Nov 15 '16 at 16:45
  • @ScottChamberlain That article is amazing! Thanks for the reference. – user183872 Nov 16 '16 at 08:46