1

I've read through this post about disposing of datasets and I still have a question about the destructor. I know that post basically says that you don't need to dispose of Datasets, Datatables, and Dataviews, but my dataset is MASSIVE, so I want to release that memory ASAP. So, my question, should I include a destructor even though the dataset will be disposed when my objects' dispose method is called? Also, explain to me again why the "bool disposing" is needed.

        public DEditUtil(DataSet dsTxData)
    {
        this.dsTxData = dsTxData;
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
                dsTxData.Dispose();

            disposed = true;
        }
    }

    ~DEditUtil()
    {
        Dispose(false);
    }
Community
  • 1
  • 1
ganders
  • 7,285
  • 17
  • 66
  • 114
  • 4
    The answer to "Should I create a finalizer" is almost always "No" – asawyer May 04 '12 at 14:03
  • 2
    Very similar to http://stackoverflow.com/questions/3882804/finalizer-and-idisposable – James Michael Hare May 04 '12 at 14:05
  • @David.Chu.ca so just leave out the GC.SuppressFinalize()? – ganders May 04 '12 at 14:06
  • 2
    If you are going to implement the pattern then you *do want* the `SuppressFinalize` to prevent your `Dispose(bool)` method from being called twice. – Michael Edenfield May 04 '12 at 14:08
  • 1
    @MichaelEdenfield - calling Dispose(bool) twice shouldn't be a problem, but having the destructor called and your DataTables moved to Gen-1 is really bad. So yes, SuppressFinalize is very essential. – H H May 04 '12 at 14:31
  • possible duplicate of [Should I Dispose() DataSet and DataTable?](http://stackoverflow.com/questions/913228/should-i-dispose-dataset-and-datatable) – H H May 04 '12 at 14:35

4 Answers4

4

Yes, in general you should implement the full IDisposable pattern whenever either of the following is true:

  1. You have unmanaged resources being allocated by your class, or
  2. You have managed resources that implement IDisposable (which implies that they, in turn, have unmanaged resources)

The presence of the finalizer (the general CLR term for what C++/C# call a "destructor") is to handle cases where your Dispose method is not called for some reason. The boolean value being passed in to your protected Dispose() method indicated if you are being called from within the public Dispose, or from within your finalizer.

If your public Dispose method is being called, that call stack is deterministic: your dispose method is being called directly, so you can safely call methods (including Dispose) on your child objects.

If you are inside of the finalizer, then you have no idea what's going on with other objects that are also being garbage-collected. In general, it may not be safe to call methods on managed objects your control from within your finalizer.

So, the boolean value basically says: "if true, dispose everything; if false, only dispose my unmanaged resources and let everyone else deal with theirs."

Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117
  • However he has no unmanaged resources. If the finalizer is called in his example code, it will call `Dispose(false)` which will not dispose his dataset. It basically does nothing in his case. If you remove the `if(disposing)` in the `Dispose(bool disposing)` method, then yes, this would help clean up the DataSet when his class' Dispose() method was not called. – CodingWithSpike May 04 '12 at 14:12
  • 1
    It never hurt to implement the full pattern, and it's much easier than trying to remember "do I need half of it? all of it?" and getting it wrong. That's why its a pattern. – Michael Edenfield May 04 '12 at 14:14
  • 1
    I do agree with that statement. I usually implement the full pattern too. I just wanted to point out that with his sample code, his DataSet's .Dispose() would not be called by his finalizer. – CodingWithSpike May 04 '12 at 14:17
  • @rally25rs yes, you are correct. If the question was "do I absolutely need a destructor" than I would have to admit that it isn't technically doing anything here. But if you ask me "should I add one", then yes, you should use the pattern the same way every time unless you have a really, really good reason not to. – Michael Edenfield May 04 '12 at 14:19
  • No, you almost never need the _full_ pattern. You should never need a destructor. And for a DataSet you don't really need anything, it's more out of a general principle that we implement IDisposable. – H H May 04 '12 at 14:27
2

The memory used by your DataSet object will be available for garbage collection as soon as it is not referenced anymore by the code.

The garbage collector will make that memory available to the program at a later (non determinate) time.

Both things do not depend of having or not a destructor or calls to Dispose, so the answer is no - you don't need a destructor.

MiMo
  • 11,793
  • 1
  • 33
  • 48
  • @MichaelEdenfield: it seems he said *exactly* the same. Object will be released when it's not more referenced by anything. May be there is tiny english words "game", but I'm not sure. – Tigran May 04 '12 at 14:13
  • 2
    I objected to the phrase "as soon as" implying that it was immediately collected when it went out of scope. That's explicitly not guaranteed by the runtime. – Michael Edenfield May 04 '12 at 14:16
  • In fact, it might not get collected until the program is shut down. So I agree, this answer is false and misleading. – Skalli May 04 '12 at 14:29
  • @MichaelEdenfield: by 'released' I meant 'available for garbage collection' - bad choice of words - I am claryfying my answer. – MiMo May 04 '12 at 14:31
  • @MichaelEdenfield - Calling Dispose() does __not__ release (collect, recycle) any memory. Dispose is about resources only. – H H May 04 '12 at 14:34
  • @HenkHolterman Yes, my comment wasn't worded much better than the original answer (I'm just going to delete it); I was conflating managed and unmanaged resources together when they're obviously treated differently here. – Michael Edenfield May 04 '12 at 14:53
1

No, you do not need any other method call here, it's already enough what you did. Dispose will be called by the runtime and you will free resource allocated, the cleanup let's leave up to GC to decide how and when to do it.

If you have really huge problems with memory you can try to cal GC.Collect() to enforce the collection of the garbage, that usually works, but it's never a good practise to use it in that way, so try to avoid it as much as possible.

EDIT

According to the comments, it's important to pay attention on execution flow in your case, cause the DataSet cleanup will be done only if it's not disposed==false and disposing == true, which from the code provided, will be a case only during esplicit call from the code.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • 3
    `Dispose()` is never "just called" by the runtime; if you follow the recommended practice of always wrapping disposable objects in a `using` block, the *compiler* will insert explicit `Dispose()` calls for you. – Michael Edenfield May 04 '12 at 14:12
  • 2
    -1 because the runtime does not call Dispose() on your objects automatically. – CodingWithSpike May 04 '12 at 14:15
  • @MichaelEdenfield: I hope the OP's is aware of this, considering that he already implemented Dispose and seems also resolved his problem, but searching for some possible "better" or more "secure" solution. – Tigran May 04 '12 at 14:15
  • @rally25rs: Where do you read in my post thta runtime call dispose *automatically* ? – Tigran May 04 '12 at 14:16
  • 1
    "Dispose will be called by the runtime" Dispose ought to be called by user code, and probably is, but it's not called by the runtime itself – Servy May 04 '12 at 14:17
  • @Servy: yes, correct. It *will* be called by runtime if you follow dispose pattern, or you need to call it esplicitly. I repeat, OP is clearly aware of this. His question in regard, is there somethign **more** to do on subject or it's enough an implementation like provided. – Tigran May 04 '12 at 14:19
  • Ah yes, I read "Dispose will be called by the runtime" as saying the `Dispose()` method is automatically called by the runtime. But I think you meant "the finalizer will be called by the runtime, which will call `Dispose(bool)`". Looks like a wording misunderstanding. I retract my -1 :) – CodingWithSpike May 04 '12 at 14:24
  • The destructor will call `dispose(false)`, which won't dispose the data set. Use code calling dispose would call `dispose(true)` and actually dispose of the data set. Since there are no unmanaged resources, the user call to `Dispose` is the only one that actually does anything (useful). That really should be called out in a good answer to this question. – Servy May 04 '12 at 14:28
  • I agree with Servy and Michael Edenfield, this answer is misleading, even if the op knows what he's doing, others reading this answer might misunderstand. – Skalli May 04 '12 at 14:33
1

Very seldom should user-written classes ever use finalizers (or C# destructors) for any purpose other than to log failures to call Dispose. Unless one is delving deep into the particulars of how finalizers work, and exactly what is or is not guaranteed guaranteed about the context in which they run, one should never call any other object's Dispose method within a finalizer. In particular, if one's object's Finalize() method is running, any IDisposable objects to which it holds a reference will usually fall into one of the following categories:

  1. Someone else still has a reference to that object and expects it to be usable, so calling `Dispose` would be bad.
  2. The object cannot be safely disposed within a finalizer thread context, so calling `Dispose` would be bad.
  3. The object would have kept the present object alive if there were anything meaningful for its `Dispose` handler to do; the fact that the present object's `Finalize` method is running implies that there's no longer any need to call `Dispose` on the other object (this scenario can occur with events).
  4. The object has already had its `Finalize` method called, so calling `Dispose` would be at best superfluous.
  5. The object is scheduled to have its `Finalize` method called, so calling `Dispose` would likely be superfluous.

Although there are a few cases where an object might need to clean up another IDisposable object within a Finalize method, using Finalize properly in such cases is tricky, and using it improperly is apt to be worse than not using it at all. Among other things, Finalize generally only runs when an entity requests an IDisposable and wrongfully fails to call Dispose before abandoning it. It's usually better to focus one's efforts on making sure that Dispose gets properly before an object is abandoned, than on trying to properly handle buggy consumer code.

supercat
  • 77,689
  • 9
  • 166
  • 211