2009-04-21 1 views
2

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

Для справки: реализация 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(), которые явно называют первым затем утилизации: = ложь и код внутри "если утилизации ..." никогда не будет выполнить, чтобы освободить управляемые ресурсы, в результате чего они останутся в памяти до следующего прохождения GC. Почему бы им не было явно освобождено? Не сделал бы так свободной памяти на первом проходе GC и не оставил ненужные объекты в памяти до следующего прохода?
  2. Почему среда IDE не генерирует вызов Dispose (false) при переопределении Finalize() в классе IDisposable?
  3. Как бы GC знал, чтобы вызвать Dispose (false), и убедиться, что это реализация IDE, а не пользовательская реализация, которая использует параметр bool другим способом? * ... и не должен Dispose (распоряжаться как bool) быть членом интерфейса, если GC проверяет его существование и использует его таким образом, который предполагает определенную реализацию (object.Dispose (disposing: = false))? * При наличии как Dispose(), так и Dispose(disposing as boolean) почему GC когда-либо выбирает вызов перегруженного, неинтерфейсного элемента?

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

ответ

3

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

Редактировать (оказывается Финалайзер вызывает Dispose (ложь))

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

+0

Спасибо, что заметили это; Я перепутал поля (теперь я исправил это). Вы также частично отвечаете на мой вопрос, заявляя, что GC не будет вызывать Dispose (false), что заставляет меня задаться вопросом, почему VS2005 использует эту перегрузку вообще. Есть ли когда-нибудь случай, когда Dispose (false) будет выполняться? Разбивается ли это VS, так что * if * I переопределяет Finalize(), тогда я могу вызвать Dispose (false)? Кажется забавным, так как IDE не заглушает какой-либо код, если я переопределяю Finalize() – STW

+0

Я отредактировал свой ответ; GC вызывается Dispose (false), как вы описываете. Причиной этого является то, что управляемые компоненты, которые будут расположены с помощью Dispise (true), будут (или, скорее, ДОЛЖНЫ быть) индивидуально завершены и удалены GC. –

+0

Интересные вещи. Я заметил, что в C# реализация интерфейса IDisposable не создает перегрузку; однако запуск анализа кода без перегрузки (либо на C#, либо на VB.NET) генерирует два предупреждения, инструктирующие вас добавить перегрузку. – STW

0

«при утилизации ..."Никогда не будет выполнять , чтобы освободить управляемые ресурсов. - в результате чего их оставшимся в памяти до следующего GC перевала Почему бы не эти явно освобожденный

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

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

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

Как бы GC знать вызвать Dispose (ложь) и убедитесь, что это реализация IDE и не Заказной реализация, которая использует параметр Их другой способ?

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

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

0

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

 
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 рутинный каждый уровень A новое имя.Обратите внимание, что в отличие от обычного шаблона Dispose этот шаблон гарантирует, что исключение, созданное производным классом «Dispose» (или отказ метода Dispose дочернего класса называть его родительским), не помешало бы родительскому классу быть удаленным.

Смежные вопросы