-
Feb 22nd, 2018, 08:12 AM
#1
Thread Starter
Fanatic Member
Proper Cleanup
I have a class (class A) that is instantiated by a consumer. Class A instantiates class B privately. Class B creates a Timers.Timer. Originally, in class B's Finalize event, I disposed of the timer and all was fine. However, class A is COM visible and when consumed by a COM application, when class A is set to Nothing, the Finalize events never fire for class B and thus my timer is still running (not properly cleaned up). Should I implement IDisposable on class A and class B and rely on the user to call .Dispose on class A, which would then call .Dispose on class B, which calls the timer's dispose method? Is that overkill? Any suggestions would be appreciated.
-
Feb 22nd, 2018, 08:48 AM
#2
Re: Proper Cleanup
Take it from me: this is a bad design and it's impossible to safely dispose of resources as-is.
I'm going to call A "outer" and B "inner". Here's the problem.
Finalizers happen in a non-deterministic order. So it's possible, in a finalization context, the GC could decide to destroy B before A. If it does this, then A cannot legally do anything with B. That is why we write the Dispose() pattern with finalizer like this:
Code:
Public Sub Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Sub Finalize()
Dispose(False)
End Sub
Protected Overrides Sub Dispose(ByVal isSafe As Boolean)
' Deal with unmanaged objects like handles.
If isSafe Then
' Deal with .NET objects.
End If
End Sub
You can only safely deal with .NET objects if you know Dispose() has been called. So this pattern protects those things and ensures you only get rid of unmanaged things in a finalizer.
Note this means it's not safe for B to dispose its Timer from a finalizer, either. While that Timer has an unmanaged resource, it is itself a .NET object so if you're in a finalizer context you have to hope its finalizer does its work.
You should strive to make it such that when the COM code is done with your .NET object, YOU call Dispose. I'm assuming the COM object has a Release(), that's probably the best place. You have to figure out if there are no references left, then call Dispose() if that's the case. This gives you a deterministic disposal order. (Or: perhaps COM could call Dispose() before releasing the object.)
If the Finalizers aren't running, maybe the COM code creates that object? Maybe in that context, the GC doesn't run or doesn't call finalizers. If that's the case, well, the last paragraph still holds true.
So yes. One of the most basic rules of IDisposable is it is viral:
If you reference an IDisposable, YOU should be IDisposable.
Since B holds a Timer, B needs to be IDisposable. Since A holds a B, A needs to be IDisposable. And so on.
This answer is wrong. You should be using TableAdapter and Dictionaries instead.
-
Feb 22nd, 2018, 09:03 AM
#3
Thread Starter
Fanatic Member
Re: Proper Cleanup
I think I got what you are saying, so for class A (outer) I have:
Code:
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
classB.Dispose()
End If
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overrides Sub Finalize()
Dispose(False)
MyBase.Finalize()
End Sub
In class B (inner) I have:
Code:
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
TimerX.Dispose()
End If
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
End Sub
A follow up question, I control the COM application that consumes class A, so I can physically call classA.Dispose() - is that what you meant? Not sure how to ensure dispose is called any other way.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|