Почему VS2005 / VB.NET реализует интерфейс IDisposable с перегрузкой Dispose (утилизировать как логическое значение)? - PullRequest
2 голосов
/ 21 апреля 2009

Недавно мне нужно было сравнить предложенный шаблон для IDisposable и финализации объекта с автоматически сгенерированным шаблоном, который мы предоставляем VS2005 / VB.NET. Мы использовали автоматически сгенерированный один из них, но, посмотрев на него два рядом, у меня возник ряд вопросов о реализации VB.NET ...

Для справки, вот реализация IDE:

Public Class Class1
    Implements IDisposable

    Private disposedValue As Boolean = False        ''// To detect redundant calls

    ''// IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ''// TODO: free managed resources when explicitly called
            End If

            ''// TODO: free shared unmanaged resources
        End If
        Me.disposedValue = True
    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

Вопросы:

  1. Если Finalize () вызывается во время GC без явного вызова сначала метода object.Dispose (), а затем утилизации: = false, и код внутри «если утилизировать ...» никогда не будет выполнен, чтобы освободить управляемые ресурсы - что приведет к ним оставаясь в памяти до следующего прохода GC. Почему они не будут явно освобождены? Разве это не освободило бы больше памяти при первом проходе GC и не оставило бы ненужные объекты в памяти до следующего прохода?
  2. Почему IDE не генерирует вызов Dispose (false) при переопределении Finalize () для класса IDisposable?
  3. Как GC узнает, что нужно вызвать Dispose (false) и убедиться, что это реализация IDE, а не пользовательская реализация, которая использует параметр bool другим способом? * ... и не должен ли Dispose (распоряжение как bool) быть элементом интерфейса, если GC проверяет его существование и использует его способом, предполагающим определенную реализацию (object.Dispose (dispose: = false))? * При наличии Dispose() и Dispose(disposing as boolean), почему GC когда-либо решил вызвать перегруженный неинтерфейсный член?

В целом меня смущает предполагаемое добавленное значение наличия расширенного пути кода, который выполняется, когда Dispose() вызывается явно (в отличие от наличия общего пути, который выполняется независимо от того, был ли Dispose() вызван явно ). Несмотря на то, что я могу оценить, что он имеет благие намерения, я не могу понять, как он делает что-либо, кроме как задержать фактическое освобождение управляемых ресурсов, если Dispose() не вызывается напрямую. По сути, кажется, что это работает только для того, чтобы сделать недоступными управляемые ресурсы на графе объектов, оставляя их в стороне до 2-го ГХ, а не освобождая их в точке, где они, как известно, больше не нужны.

Ответы [ 3 ]

3 голосов
/ 21 апреля 2009

В вашем вопросе есть логическая ошибка ... если в финализаторе вызывается Dispose(), тогда да, disposedValue будет ложным, что означает, что If Not Me.disposedValue Then... будет выполнено Параметр, переданный для disposing, равен true, поэтому весь код в нем должен выполняться просто отлично.

Редактировать (оказывается, что финализатор вызывает Dispose (false))

Финализатор в форме (который запускается, только если Dispose() никогда не вызывается в форме) вызывает Dispose(false). Причина этого в том, что Form в настоящее время находится в GC. В результате, МЕНЕДЖЕРНЫЕ ресурсы (то есть компоненты Form) будут собраны, и их собственные финализаторы должны вызвать Dispose(), если потребуется. Только неуправляемые ресурсы должны быть освобождены в Dispose(false).

0 голосов
/ 17 января 2011

Этот шаблон был создан, когда ожидалось, что многие классы обернут как управляемые, так и неуправляемые ресурсы, и что производные классы могут добавить неуправляемые ресурсы в класс. С тех пор Microsoft признала, что лучше не оборачивать неуправляемые ресурсы в их собственные классы, чем объединять их с другими классами, и написала класс SafeHandle, чтобы облегчить это. Могут быть некоторые веские причины иметь шаблон IDisposable, где не переопределяемый метод вызывает переопределяемый (например, внешний уровень может использовать Interlocked.Exchange, чтобы гарантировать, что Dispose вызывается только один раз). К сожалению, в .net нет чистого способа для одновременного переопределения и затенения метода; в противном случае идеальным шаблоном для каждого класса IDisposable было бы предоставление нового защищенного переопределяемого метода, который переопределял бы его метод суперкласса:

Class Blah1
    Sub Dispose()
        Try
            Dispose1()
        Finally

        End Try
        ' Blah1 cleanup here
    End Sub
    Protected Overridable Sub Dispose1()
        ' Child-level code will override this
    End Sub
End Class
Class Blah2
    Inherits Blah1
    Protected NotOverridable Overrides Sub Dispose1()
        Try
            Dispose1()
        Finally

        End Try
        ' Blah2 cleanup here
    End Sub
    Protected Overridable Sub Dispose2()
        ' Child-level code will override this
    End Sub
End Class

Шаблон был бы чище, если бы не нужно было назначать подпрограмме каждого уровня новое имя. Обратите внимание, что в отличие от обычного шаблона Dispose, этот шаблон гарантирует, что исключение, выброшенное в методе Dispose производного класса (или сбой метода Dispose дочернего элемента для вызова его родительского), не будет препятствовать удалению родительского класса.

0 голосов
/ 25 сентября 2010

"если избавиться ..." никогда не выполнится освободить управляемый ресурсы - в результате чего они остаются в памяти до следующего прохода GC. Зачем Разве они не будут явно освобождены?

«Если утилизация» не будет выполняться, но управляемые ресурсы будут освобождены на этом этапе GC (если они действительно имеют право на освобождение). К тому времени, когда ваш объект завершается, он не может быть достигнут никакими другими живыми объектами, поэтому любые дочерние объекты не будут иметь права на сбор, поскольку ваш объект все еще ссылается на них.

Почему IDE не генерирует вызов Dispose (false) при переопределении Finalize () для класса IDisposable?

Они, вероятно, просто не добавили специальный случай для проверки на Dispose при создании заглушки переопределения для Finalizer. Кроме того, IDE не генерирует финализатор автоматически, поскольку большинству классов не нужен финализатор. Единственный раз, когда у вас должен быть финализатор, это если вы непосредственно владеете неуправляемым ресурсом. Путь Dispose (false) может все еще вызываться производным классом, который владеет неуправляемым ресурсом и, следовательно, нуждается в финализаторе. Возможность производного финализатора также является причиной того, что вызов метода GC.SuprressFinalize всегда должен присутствовать в базовом методе Dispose ().

Как GC узнает, чтобы вызвать Dispose (false) и убедиться, что это реализация IDE, а не пользовательская реализация, которая использует Параметр bool по-другому?

Это не так. GC знает о Finalize и только Finalize. Одноразовый - это шаблон для программиста , а не сборщика мусора . Вот почему шаблон требует написания финализатора, который вызывает Dispose (false) самостоятельно.

Причиной наличия двух путей является то, что метод Dispose можно назвать двумя способами. Обычный опыт заключается в том, чтобы вызывать его явно, после чего все управляемые объекты существуют, и истинный путь избавится от них и освободит любые неуправляемые ресурсы. Ложный путь будет использоваться при вызове финализатора, и в этот момент вы не можете предполагать, что ни в одном из ваших полей еще не были вызваны их собственные финализаторы. Разделенный путь не влияет на граф объектов или на то, когда будут собраны содержащиеся объекты.

...