I came up with a template that seems pretty good. This inspired someone posted on CodeProject.com - using a list to track supplies; raiiBase (of T) is a base class suitable for any class whose constructor takes one parameter. The constructor of the class must be protected, and the construction must be performed using the factory method. The static constructor makeRaii () takes a delegate to the factory function, which should accept Stack (iDisposable) along with the parameter of the expected type of the class. Usage example:
Public Class RaiiTest
Inherits raiiBase (Of String)
Dim thing1 As testDisposable = RAII (New testDisposable ("Moe" & creationParam, "a"))
Dim thing2 As testDisposable = RAII (New testDisposable ("Larry" & creationParam, "b"))
Dim thing3 As testDisposable = RAII (New testDisposable ("Shemp" & creationParam, "c"))
Dim thing4 As testDisposable = RAII (New testDisposable ("Curly" & creationParam, "d"))
Protected Sub New (ByVal dispList As Stack (Of IDisposable), ByVal newName As String)
MyBase.New (dispList, newName)
End sub
Private Shared Function _newRaiiTest (ByVal dispList As Stack (Of IDisposable), ByVal theName As String) As RaiiTest
Return New RaiiTest (dispList, theName)
End function
Public Shared Function newRaiiTest (ByVal theName As String) As RaiiTest
Return makeRaii (Of RaiiTest) (AddressOf _newRaiiTest, theName)
End function
Shared Sub test (ByVal st As String)
Try
Using it As RaiiTest = newRaiiTest (st)
Debug.Print ("Now using object")
End using
Debug.Print ("No exceptions thrown")
Catch ex as raiiException
Debug.Print ("Output exception:" & ex.Message)
If ex.InnerException IsNot Nothing Then Debug.Print ("Inner exception:" & ex.InnerException.Message)
For Each exx As Exception In ex.DisposalExceptions
Debug.Print ("Disposal exception:" & exx.Message)
Next
Catch ex as exception
Debug.Print ("Misc. Exception:" & ex.Message)
End try
End sub
End class
Since raiiTest inherits raiiBase (from String) to create an instance of the class, call newRaiiTest with a string parameter. RAII () is a generic function that will register its argument as iDisposable, which will need to be cleared and then returned. All registered disposable items will be selected if either Dispose is called on the main object, or when an exception is thrown in the structure of the main object.
Here's the riaaBase class:
Option Strict On Class raiiException Inherits Exception ReadOnly _DisposalExceptions () As Exception Sub New (ByVal message As String, ByVal InnerException As Exception, ByVal allInnerExceptions As Exception ()) MyBase.New (message, InnerException) _DisposalExceptions = allInnerExceptable Read Subne DisposalExceptions () As Exception () Get Return _DisposalExceptions End Get End Property End Class Public Class raiiBase (Of T) Implements IDisposable Protected raii List As Stack (Of IDisposable) Protected creation Param As T Delegate Function raiiFactory (Of TT As raiiBase (Of T)) (ByVal theList As Stack (Of IDisposable), ByVal theParam As T) As TT Shared Function CopyFirstParamToSecondAndReturnFalse (Of TT) (ByVal P1 As TT, ByRef P2 As TT) As Boolean P2 = P1 Return False End Function Shared Function makeRaii (Of TT As raiiBase (Of T)) (ByVal theFactory As raiiFactory (Of TT), ByVal theParam As T) As TT Dim dispList As New Stack (Of IDisposable) Dim constructionFailureExcept ion As Exception = Nothing Try Return theFactory (dispList, theParam) Catch ex As Exception When CopyFirstParamToSecondAndReturnFalse (ex, constructionFailureException) 'The above statement let us find out what exception occurred without having to catch and rethrow Throw' Should never happen, since we should have returned false above Finally If constructionFailureException IsNot Nothing Then zapList (dispList, constructionFailureException) End If End Try End Function Protected Sub New (ByVal DispList As Stack (Of IDisposable), ByVal Params As T) Me.raiiList = DispList Me.creationParam = Params End Sub Public Shared Sub zapList (ByVal dispList As IEnumerable (Of IDisposable), ByVal triggerEx As Exception) Using theEnum As IEnumerator (Of IDisposable) = dispList.GetEnumerator Try While theEnum.MoveNext theEnum.Current.Dispose () End While Catch ex Exception Dim exList As New List (Of Exception) exList.Add (ex) While theEnum.MoveNext Try theEnum.Current.Dispose () Catch ex2 As Exception exList.A dd (ex2) End Try End While Throw New raiiException ("RAII failure", triggerEx, exList.ToArray) End Try End Using End Sub Function RAII (Of U As IDisposable) (ByVal Thing As U) As U raiiList.Push (Thing ) Return Thing End Function Shared Sub zap (ByVal Thing As IDisposable) If Thing IsNot Nothing Then Thing.Dispose () End Sub Private raiiBaseDisposeFlag As Integer = 0 'To detect redundant calls' IDisposable Protected Overridable Sub Dispose (ByVal disposing As Boolean) If disposing AndAlso Threading.Interlocked.Exchange (raiiBaseDisposeFlag, 1) = 0 Then zapList (raiiList, Nothing) End If End Sub #Region "IDisposable Support" 'This code added by Visual Basic to correctly implement the disposable pattern. Public Sub Dispose () Implements IDisposable.Dispose 'Do not change this code. Put cleanup code in Dispose (ByVal disposing As Boolean) above. Dispose (True) GC.SuppressFinalize (Me) End Sub #End Region End Class Note that a custom exception type will be thrown if deletion fails for any or all registered disposable objects. An InnerException indicates whether the constructor failed; To see which utilities failed to execute, check ExposalExceptions.