失败初始化或构造处理的IDisposable [英] Handling iDisposable in failed initializer or constructor

查看:155
本文介绍了失败初始化或构造处理的IDisposable的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有在.net中的任何漂亮的图案确保由对象所拥有的IDisposable领域将得到处理,如果有异常施工过程中引发的,可能在一个字段初始值?环绕字段初始在try / catch块的唯一方法是,如果该块是在调用构造函数,这将使清理code妥善处置任何它相当困难之外。

Is there any nice pattern in .Net for ensuring that iDisposable fields owned by an object will get disposed if an exception is thrown during construction, possibly during a field initializer? The only way to surround field initializers in a Try/Catch block is if the block is outside the call to the constructor, which will make it rather difficult for cleanup code to properly dispose of anything.

我可以计算的唯一方法是将对象从它的构造函数类似IDisposable的数组的基类继承,并设置第一个项目要在该数组指向自身。所有构造函数的子类应该是私人或Orotected,并包括参数。实例化应该是通过工厂方法,这将声明一个IDisposable的数组,并把它传递给适当的构造。如果构造失败,工厂方法将有一个参考的局部构造的对象,它可以再处置(Dispose方法必须的,当然,是ppared接受该对象可能不完全构造的可能性$ P $ )。

The only approach I can figure would be to the object inherit from a base class whose constructor takes something like an array of iDisposable, and sets the first item in that array to point to itself. All constructors the descendant classes should be Private or Orotected, and include that parameter. Instantiation should be via factory methods, which will declare an array of one iDisposable and pass it to the appropriate constructor. If the constructor fails, the factory method will have a reference to the partially-constructed object, which it can then dispose (the dispose method must, of course, be prepared to accept the possibility that the object may not be fully constructed).

该方法可以通过使物体保持一个列表IDisposable的对象,它创建,使物体被清理,而无需显式地处理每一个扩展;这样的列表将与工厂方法,调用,处置方法相结合是有用的,但在很大程度上是正交。

The approach could be extended by having the object keep a list of iDisposable objects it creates, to allow the objects to be cleaned up without having to explicitly dispose each one; such a list would be useful in conjunction with the factory-method-calls-dispose approach, but is largely orthogonal to it.

有什么想法?

推荐答案

我已经出来,似乎pretty的良好格局。它的设计灵感来自张贴在codeProject.com一个人 - 使用列表来跟踪一次性的; raiiBase(T)适合任何类,其构造函数一个参数的基类。类构造函数必须受到保护,施工必须通过工厂方法来完成。静态makeRaii()构造函数委托给一个工厂函数,它必须接受的栈(IDisposable接口),以及类的期望类型的参数。示例用法:

I've come up with a pattern that seems pretty good. It's inspired by an someone posted on CodeProject.com--using a list to keep track of disposables; raiiBase(of T) is a base class suitable for any class whose constructor takes a single parameter. The class constructor must be protected, and construction must be done via factory method. The static makeRaii() constructor takes a delegate to a factory function, which must accept a Stack(of iDisposable) along with a parameter of the class's expected type. A sample usage:


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

由于raiiTest继承raiiBase(字符串),来创建一个类的实例,调用newRaiiTest一个字符串参数。 RAII()是一个通用的功能,将它的参数注册为一个IDisposable接口,将需要清理,然后再返回。无论何时调用Dispose的主要对象所有注册一次性将被处置,或者当一个异常被抛出的主要对象的构造。

Since raiiTest inherits raiiBase(of String), to create a class instance, call newRaiiTest with a string parameter. RAII() is a generic function that will register its argument as an iDisposable that will need cleaning up, and then return it. All registered disposables will be Disposed when either Dispose is called on the main object, or when an exception is thrown in the construction of the main object.

下面是riaaBase类:

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 = 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

请注意,如果处理失败,任何注册的一次性或全部对象的自定义异常的类型将被抛出。的InnerException将指示构造是否失败;看看哪些处置(S)失败,请检查DisposalExceptions。

Note that a custom exception type will be thrown if disposal fails for any or all of the registered disposable objects. InnerException will indicate whether the constructor failed; to see which disposer(s) failed, check DisposalExceptions.

这篇关于失败初始化或构造处理的IDisposable的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆