Tôi đã đưa ra một mẫu có vẻ khá tốt. Nó lấy cảm hứng từ một người nào đó được đăng trên CodeProject.com - sử dụng danh sách để theo dõi các lần gỡ bỏ; raiiBase (của T) là một lớp cơ sở phù hợp với bất kỳ lớp nào mà hàm tạo của nó có một tham số đơn. Trình xây dựng lớp phải được bảo vệ và việc xây dựng phải được thực hiện thông qua phương thức factory. Hàm tạo makeRaii() tĩnh nhận một đại biểu đến một hàm nhà máy, phải chấp nhận một Stack (của iDisposable) cùng với một tham số của kiểu dự kiến của lớp.Sử dụng mẫu:
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
Vì raiiTest kế thừa raiiBase (của chuỗi), để tạo một thể hiện lớp, hãy gọi newRaiiTest với tham số chuỗi. RAII() là một hàm chung sẽ đăng ký đối số của nó như là một iDisposable mà sẽ cần dọn dẹp, và sau đó trả về nó. Tất cả các lần gỡ bỏ đã đăng ký sẽ được xử lý khi Dispose được gọi trên đối tượng chính hoặc khi một ngoại lệ được ném vào việc xây dựng đối tượng chính.
Đây là lớp riaaBase:
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 = allInnerExceptions
End Sub
Public Overridable ReadOnly Property DisposalExceptions() As Exception()
Get
Return _DisposalExceptions
End Get
End Property
End Class
Public Class raiiBase(Of T)
Implements IDisposable
Protected raiiList As Stack(Of IDisposable)
Protected creationParam 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 constructionFailureException 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 As Exception
Dim exList As New List(Of Exception)
exList.Add(ex)
While theEnum.MoveNext
Try
theEnum.Current.Dispose()
Catch ex2 As Exception
exList.Add(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
Lưu ý rằng một loại ngoại lệ tùy chỉnh sẽ được ném nếu xử lý lỗi vì bất kỳ hoặc tất cả các đối tượng dùng một lần đăng ký. InnerException sẽ cho biết liệu hàm tạo có bị lỗi hay không; để xem (các) trình xử lý nào không thành công, hãy kiểm tra DisposalExceptions.
-1 Điều này không có tác dụng nếu hàm tạo đưa ra một ngoại lệ. Đối tượng mới không bao giờ được trả về, vì vậy không có gì để gọi Dispose on. – chilltemp
Hmmmm ... bị vỡ. –