Implementando finalizar e Dispose para limpeza de recursos não gerenciados
Observação
Para obter informações sobre finalizando e descarte de recursos usando o C++, consulte Destructors and Finalizers in Visual C++.
Instâncias da classe geralmente encapsulam o controle sobre os recursos que não são gerenciados pelo tempo de execução, como identificadores de janela (HWND), conexões de banco de dados e assim por diante. Portanto, você deve fornecer um explícito e uma maneira implícita para liberar esses recursos. Fornecer controle implícito, Implementando o protegido Finalize em um objeto (sintaxe do destruidor em C# e C++). O coletor de lixo chama esse método em algum momento depois que não existem mais quaisquer referências válidas para o objeto.
Em alguns casos, convém fornecer os programadores que usam um objeto com a capacidade de liberar explicitamente esses recursos externos, antes que o coletor de lixo libera o objeto. Se um recurso externo escassos ou dispendiosa, melhor desempenho pode ser obtido se o programador explicitamente libera recursos quando não estão sendo usados. Para fornecer controle explícito, implementar a Dispose fornecida pelo IDisposable. O consumidor do objeto deve chamar esse método quando ele for concluído usando o objeto. Dispose pode ser chamado, mesmo se as outras referências ao objeto estão funcionais.
Observe que mesmo quando você fornece o controle explícito usando Dispose, você deve fornecer a limpeza implícita usando a Finalize método. Finalizar fornece um backup para impedir que recursos permanentemente vazando se o programador falhar chamar Dispose.
Para obter mais informações sobre como implementar Finalize e Dispose para limpar os recursos não gerenciados, consulte Coleta de Lixo. O exemplo a seguir ilustra o padrão de design básica para a implementação de Dispose. Este exemplo requer a System namespace.
' Design pattern for a base class.
Public Class Base
Implements IDisposable
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
' Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
disposed = True
End If
' Free your own state (unmanaged objects).
' Set large fields to null.
End If
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Dispose (False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
' Field to handle multiple calls to Dispose gracefully.
Dim disposed as Boolean = false
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Release managed resources.
End If
' Release unmanaged resources.
' Set large fields to null.
disposed = True
End If
' Call Dispose on your base class.
Mybase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
// Design pattern for a base class.
public class Base: IDisposable
{
private bool disposed = false;
//Implement IDisposable.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Dispose (false);
}
}
// Design pattern for a derived class.
public class Derived: Base
{
private bool disposed = false;
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Release managed resources.
}
// Release unmanaged resources.
// Set large fields to null.
// Call Dispose on your base class.
disposed = true;
}
base.Dispose(disposing);
}
// The derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
}
O código a seguir expande o exemplo anterior para mostrar as diferentes maneiras de Dispose é invocado e quando Finalize é chamado. Os estágios do descarte padrão são controlados com saída para o console. A alocação e liberação de um recurso não gerenciado é tratado na classe derivada.
Imports System
Imports System.Collections.Generic
Imports System.Runtime.InteropServices
' Design pattern for a base class.
Public MustInherit Class Base
Implements IDisposable
Private disposed as Boolean = false
Private instName As String
Private trackingList As List(Of Object)
Public Sub New(instanceName As String, tracking As List(Of Object))
MyClass.instName = instanceName
trackingList = tracking
trackingList.Add(Me)
End Sub
Public ReadOnly Property InstanceName() As String
Get
Return instName
End Get
End Property
'Implement IDisposable.
Public Overloads Sub Dispose() Implements IDisposable.Dispose
Console.WriteLine(vbNewLine + "[{0}].Base.Dispose()", instName)
Dispose(true)
GC.SuppressFinalize(Me)
End Sub
Protected Overloads Overridable Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
' Free other state (managed objects).
Console.WriteLine("[{0}].Base.Dispose(true)", instName)
trackingList.Remove(Me)
Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
instanceName, MyClass.GetHashCode())
Else
Console.WriteLine("[{0}].Base.Dispose(false)", instName)
End If
disposed = True
End If
End Sub
Protected Overrides Sub Finalize()
' Simply call Dispose(False).
Console.WriteLine(vbNewLine + "[{0}].Base.Finalize()", instName)
Dispose(False)
End Sub
End Class
' Design pattern for a derived class.
Public Class Derived
Inherits Base
Private disposed as Boolean = false
Private umResource As IntPtr
Public Sub New(instanceName As String, tracking As List(Of Object))
MyBase.New(instanceName, tracking)
' Save the instance name as an unmanaged resource
umResource = Marshal.StringToCoTaskMemAuto(instanceName)
End Sub
Protected Overloads Overrides Sub Dispose(disposing As Boolean)
If disposed = False Then
If disposing Then
Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName)
' Release managed resources.
Else
Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName)
End If
' Release unmanaged resources.
If umResource <> IntPtr.Zero
Marshal.FreeCoTaskMem(umResource)
Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}", _
InstanceName, umResource.ToInt64())
umResource = IntPtr.Zero
End If
disposed = True
End If
' Call Dispose in the base class.
MyBase.Dispose(disposing)
End Sub
' The derived class does not have a Finalize method
' or a Dispose method without parameters because it inherits
' them from the base class.
End Class
Public Class TestDisposal
Public Shared Sub Main()
Dim tracking As New List(Of Object)()
' Dispose is not called, Finalize will be called later.
Using Nothing
Console.WriteLine(vbNewLine + "Disposal Scenario: #1" + vbNewLine)
Dim d3 As New Derived("d1", tracking)
End Using
' Dispose is implicitly called in the scope of the using statement.
Using d1 As New Derived("d2", tracking)
Console.WriteLine(vbNewLine + "Disposal Scenario: #2" + vbNewLine)
End Using
' Dispose is explicitly called.
Using Nothing
Console.WriteLine(vbNewLine + "Disposal Scenario: #3" + vbNewLine)
Dim d2 As New Derived("d3", tracking)
d2.Dispose()
End Using
' Again, Dispose is not called, Finalize will be called later.
Using Nothing
Console.WriteLine(vbNewLine + "Disposal Scenario: #4" + vbNewLine)
Dim d4 As New Derived("d4", tracking)
End Using
' List the objects remaining to dispose.
Console.WriteLine(vbNewLine + "Objects remaining to dispose = {0:d}", tracking.Count)
For Each dd As Derived in tracking
Console.WriteLine(" Reference Object: {0:s}, {1:x16}",
dd.InstanceName, dd.GetHashCode())
Next dd
' Queued finalizers will be exeucted when Main() goes out of scope.
Console.WriteLine(vbNewLine + "Dequeueing finalizers...")
End Sub
End Class
' The program will display output similar to the following:
'
' Disposal Scenario: #1
'
'
' Disposal Scenario: #2
'
'
' [d2].Base.Dispose()
' [d2].Derived.Dispose(true)
' [d2] Unmanaged memory freed at 00000000001ce420
' [d2].Base.Dispose(true)
' [d2] Removed from tracking list: 0000000002bf8098
'
' Disposal Scenario: #3
'
'
' [d3].Base.Dispose()
' [d3].Derived.Dispose(true)
' [d3] Unmanaged memory freed at 00000000001ce420
' [d3].Base.Dispose(true)
' [d3] Removed from tracking list: 0000000000bb8560
'
' Disposal Scenario: #4
'
'
' Objects remaining to dispose = 2
' Reference Object: d1, 000000000297b065
' Reference Object: d4, 0000000003553390
'
' Dequeueing finalizers...
'
' [d4].Base.Finalize()
' [d4].Derived.Dispose(false)
' [d4] Unmanaged memory freed at 00000000001ce420
' [d4].Base.Dispose(false)
'
' [d1].Base.Finalize()
' [d1].Derived.Dispose(false)
' [d1] Unmanaged memory freed at 00000000001ce3f0
' [d1].Base.Dispose(false)
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
// Design pattern for a base class.
public abstract class Base : IDisposable
{
private bool disposed = false;
private string instanceName;
private List<object> trackingList;
public Base(string instanceName, List<object> tracking)
{
this.instanceName = instanceName;
trackingList = tracking;
trackingList.Add(this);
}
public string InstanceName
{
get
{
return instanceName;
}
}
//Implement IDisposable.
public void Dispose()
{
Console.WriteLine("\n[{0}].Base.Dispose()", instanceName);
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
Console.WriteLine("[{0}].Base.Dispose(true)", instanceName);
trackingList.Remove(this);
Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
instanceName, this.GetHashCode());
}
else
{
Console.WriteLine("[{0}].Base.Dispose(false)", instanceName);
}
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Console.WriteLine("\n[{0}].Base.Finalize()", instanceName);
Dispose(false);
}
}
// Design pattern for a derived class.
public class Derived : Base
{
private bool disposed = false;
private IntPtr umResource;
public Derived(string instanceName, List<object> tracking) :
base(instanceName, tracking)
{
// Save the instance name as an unmanaged resource
umResource = Marshal.StringToCoTaskMemAuto(instanceName);
}
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName);
// Release managed resources.
}
else
{
Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName);
}
// Release unmanaged resources.
if (umResource != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(umResource);
Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}",
InstanceName, umResource.ToInt64());
umResource = IntPtr.Zero;
}
disposed = true;
}
// Call Dispose in the base class.
base.Dispose(disposing);
}
// The derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
}
public class TestDisposal
{
public static void Main()
{
List<object> tracking = new List<object>();
// Dispose is not called, Finalize will be called later.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #1\n");
Derived d3 = new Derived("d1", tracking);
}
// Dispose is implicitly called in the scope of the using statement.
using (Derived d1 = new Derived("d2", tracking))
{
Console.WriteLine("\nDisposal Scenario: #2\n");
}
// Dispose is explicitly called.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #3\n");
Derived d2 = new Derived("d3", tracking);
d2.Dispose();
}
// Again, Dispose is not called, Finalize will be called later.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #4\n");
Derived d4 = new Derived("d4", tracking);
}
// List the objects remaining to dispose.
Console.WriteLine("\nObjects remaining to dispose = {0:d}", tracking.Count);
foreach (Derived dd in tracking)
{
Console.WriteLine(" Reference Object: {0:s}, {1:x16}",
dd.InstanceName, dd.GetHashCode());
}
// Queued finalizers will be exeucted when Main() goes out of scope.
Console.WriteLine("\nDequeueing finalizers...");
}
}
// The program will display output similar to the following:
//
// Disposal Scenario: #1
//
//
// Disposal Scenario: #2
//
//
// [d2].Base.Dispose()
// [d2].Derived.Dispose(true)
// [d2] Unmanaged memory freed at 000000000034e420
// [d2].Base.Dispose(true)
// [d2] Removed from tracking list: 0000000002bf8098
//
// Disposal Scenario: #3
//
//
// [d3].Base.Dispose()
// [d3].Derived.Dispose(true)
// [d3] Unmanaged memory freed at 000000000034e420
// [d3].Base.Dispose(true)
// [d3] Removed from tracking list: 0000000000bb8560
//
// Disposal Scenario: #4
//
//
// Objects remaining to dispose = 2
// Reference Object: d1, 000000000297b065
// Reference Object: d4, 0000000003553390
//
// Dequeueing finalizers...
//
// [d4].Base.Finalize()
// [d4].Derived.Dispose(false)
// [d4] Unmanaged memory freed at 000000000034e420
// [d4].Base.Dispose(false)
//
// [d1].Base.Finalize()
// [d1].Derived.Dispose(false)
// [d1] Unmanaged memory freed at 000000000034e3f0
// [d1].Base.Dispose(false)
Para obter um exemplo de código adicional que ilustra o padrão de design para a implementação de Finalize e Dispose, consulte a implementação de um método Dispose.
Personalizando o nome de um método Dispose
Ocasionalmente, um nome de domínio específico é mais apropriado do que Dispose. Por exemplo, o encapsulamento do arquivo poderá usar o nome do método Fechar. Nesse caso, implementar Dispose em particular e criar um relatório público Fechar método que chama Dispose. O exemplo de código a seguir ilustra esse padrão. Você pode substituir Fechar com o nome de um método apropriado para seu domínio. Este exemplo requer a System namespace.
' Do not make this method overridable.
' A derived class should not be allowed
' to override this method.
Public Sub Close()
' Call the Dispose method with no parameters.
Dispose()
End Sub
// Do not make this method virtual.
// A derived class should not be allowed
// to override this method.
public void Close()
{
// Call the Dispose method with no parameters.
Dispose();
}
Finalizar
As regras seguintes descrevem as diretrizes de uso para o Finalize método:
Implementar Finalize somente em objetos que necessitam de finalização. Existem custos associados de desempenho Finalize métodos.
Se você precisar de um Finalize método, considere a implementação de IDisposable para permitir que usuários de sua classe evitar o custo de chamar o Finalize método.
Não faça a Finalize método mais visível. Ele deve ser protegido, e não pública.
Um objeto Finalize método deve liberar quaisquer recursos externos que é proprietária do objeto. Além disso, um Finalize método deve liberar apenas recursos que o objeto foi mantido. O Finalize método não deve fazer referência a quaisquer outros objetos.
Não diretamente fazer chamada um Finalize método em um objeto diferente, por exemplo, a classe base do objeto. Isso não é uma operação válida na C# linguagem de programação.
Chamar o Finalize da classe base o método de um objeto Finalize método.
Observação
A classe base Finalize método é chamado automaticamente com a sintaxe do destruidor de C# e C++.
Dispose
As regras seguintes descrevem as diretrizes de uso para o Dispose método:
Implemente o padrão dispose de design em um tipo que encapsula recursos explicitamente precisam ser liberado. Os usuários podem liberar recursos externos, chamando o público Dispose método.
Implemente o padrão dispose de design em um tipo de base que normalmente tem tipos derivados que mantêm os recursos, mesmo se o tipo base não. Se o tipo base tem um Fechar método, geralmente, isso indica a necessidade de implementar Dispose. Em tais casos, não implementam um Finalize método no tipo base. Finalizar deve ser implementado em todos os tipos derivados apresentam recursos que exigem limpeza.
Liberar quaisquer recursos descartáveis proprietária de um tipo em seu Dispose método.
Depois de Dispose foi chamado em uma instância, impedir a Finalize método execução chamando o GC.SuppressFinalize. A exceção a essa regra é a rara situação em que o trabalho deve ser feito em Finalize que não é coberto pelo Dispose.
Chame a classe base Dispose método se ele implementa IDisposable.
Não assuma que Dispose será chamado. Recursos não gerenciados por um tipo de propriedade também devem ser lançados em um Finalize método no caso de Dispose não é chamado.
Lançar um ObjectDisposedException dos métodos de instância deste tipo (diferente de Dispose) quando os recursos já são descartados. Esta regra não se aplica para o Dispose método porque deveria ser chamado várias vezes sem gerar uma exceção.
Propagar as chamadas para Dispose por meio da hierarquia de tipos base. O Dispose método deve liberar todos os recursos mantidos por este objeto e qualquer objeto que pertence a este objeto. Por exemplo, você pode criar um objeto como um TextReader que se mantiver um Stream e um Encoding, que são criados pelo TextReader sem conhecimento de. o usuário Além disso, tanto o Stream e o Encoding pode adquirir recursos externos. Quando você chamar o Dispose método no TextReader, por sua vez, ela deve chamar Dispose na Stream e o Encoding, fazendo com que eles liberar seus recursos externos.
Considere a possibilidade de não permitir que um objeto a ser usado após sua Dispose método foi chamado. Recriar um objeto que já foi descartado é um padrão difícil de implementar.
Permitir que um Dispose método a ser chamado mais de uma vez sem gerar uma exceção. O método deve fazer nada após a primeira chamada.
Portions Copyright 2005 Microsoft Corporation. Todos os direitos reservados.
Portions Copyright Addison-Wesley Corporation. Todos os direitos reservados.
Para obter mais informações sobre as diretrizes de design, consulte a "diretrizes de Design do Framework: Convenções, idiomas e padrões de reutilizável.Bibliotecas de rede" catálogo por Krzysztof Cwalina e Brad Abrams, publicado pela Addison-Wesley, 2005.
Consulte também
Referência
Conceitos
Outros recursos
Diretrizes de Design para desenvolvimento bibliotecas de classe