Este artículo se tradujo automáticamente. Para ver el artículo en inglés, active la casilla Inglés. Además, puede mostrar el texto en inglés en una ventana emergente si mueve el puntero del mouse sobre el texto.
Traducción
Inglés

Método Object.Finalize ()

 

Publicado: octubre de 2016

Permite que un objeto intente liberar recursos y realizar otras operaciones de limpieza antes de ser reclamado por el recolector de basura.

Espacio de nombres:   System
Ensamblado:  mscorlib (en mscorlib.dll)

protected virtual void Finalize()

El Finalize método se utiliza para realizar operaciones de limpieza en los recursos no administrados mantenidos por el objeto actual antes de que se destruya el objeto. El método está protegido y, por tanto, es accesible sólo a través de esta clase o una clase derivada.

En esta sección:

La Object clase no proporciona una implementación para el Finalize método y el recolector de elementos no utilizados no marcar los tipos derivados de Object para la finalización a menos que invalide la Finalize método.

Si un tipo reemplaza el Finalize método, el recolector de elementos no utilizados agrega una entrada para cada instancia del tipo a una estructura interna denominada cola de finalización. La cola de finalización contiene entradas para todos los objetos del montón administrado cuyo código de finalización se debe ejecutar antes de que el recolector de elementos no utilizados puede reclamar su memoria. El recolector de elementos no utilizados, a continuación, llama el Finalize método automáticamente en las siguientes condiciones:

  • Después de que el recolector de elementos no utilizados ha detectado que un objeto es inaccesible, a menos que el objeto se ha excluido de la finalización mediante una llamada a la GC.SuppressFinalize (método).

  • Durante el cierre de un dominio de aplicación, a menos que el objeto está exento de la finalización. Durante el cierre, incluso objetos que siguen siendo accesibles finalizados.

Finalize se llama automáticamente sólo una vez en una instancia determinada, a menos que el objeto se registre de nuevo mediante un mecanismo como GC.ReRegisterForFinalize y GC.SuppressFinalize método no se ha llamado posteriormente.

Finalize las operaciones tienen las siguientes limitaciones:

  • El tiempo exacto cuando se ejecuta el finalizador no está definido. Para garantizar la liberación determinista de recursos para implementar instancias de la clase, un Close método o proporcionar un IDisposable.Dispose implementación.

  • No se garantiza que los finalizadores de dos objetos ejecutar en un orden determinado, incluso si un objeto que hace referencia a otro. Es decir, si el objeto tiene una referencia al objeto B y ambos tienen finalizadores, objeto B podría ya ha finalizado cuando se inicia el finalizador del objeto.

  • El subproceso en el que se ejecuta el finalizador no está especificado.

El Finalize método podría no ejecutarse hasta su finalización o podría no ejecutarse en las siguientes circunstancias excepcionales:

  • Si otro finalizador se bloquea de forma indefinida (pasa a un bucle infinito, intenta obtener un bloqueo, nunca puede obtener y así sucesivamente). Dado que el tiempo de ejecución intenta ejecutar los finalizadores hasta su finalización, podrían no se llame a otros finalizadores si un finalizador se bloquea de forma indefinida.

  • Si el proceso termina sin que el tiempo de ejecución la oportunidad de limpiar. En este caso, la primera notificación de terminación del proceso de tiempo de ejecución es una notificación DLL_PROCESS_DETACH.

El tiempo de ejecución continúa finalizar los objetos durante el cierre sólo si el número de objetos susceptibles de finalización sigue reduciéndose.

Si Finalize o un reemplazo de Finalize produce una excepción y el tiempo de ejecución no está hospedado por una aplicación que reemplace la directiva predeterminada, el runtime finaliza el proceso y no activa try/finally se ejecutan los finalizadores ni bloques. Este comportamiento garantiza la integridad del proceso si el finalizador no puede liberar ni destruir recursos.

Debe invalidar Finalize para una clase que utiliza recursos no administrados como identificadores de archivo o las conexiones que deben liberarse cuando se descarta el objeto administrado que se utiliza durante la recolección de elementos no utilizados de la base de datos.

System_CAPS_importantImportante

Si un SafeHandle hay un objeto que contiene el recurso no administrado, la alternativa recomendada es implementar el patrón dispose con identificadores seguros y no reemplazar Finalize. Para obtener más información, consulte alternativa SafeHandle el sección.

El Object.Finalize método no hace nada de forma predeterminada, pero se debe invalidar Finalize sólo si es necesario y sólo para liberar recursos no administrados. Reclamar memoria suele tardar mucho más tiempo si se ejecuta una operación de finalización, porque requiere al menos dos recolecciones de elementos no utilizados. Además, debe reemplazar el Finalize solo tipos de método de referencia. Common language runtime finaliza sólo los tipos de referencia. Omite los finalizadores en los tipos de valor.

Todas las implementaciones de Finalize en un tipo derivado debe llamar a la implementación de su tipo base de Finalize. Este es el único caso en la aplicación que se puede llamar a código Finalize.

System_CAPS_noteNota

El compilador de C# no permite reemplazar la Finalize método. En su lugar, proporcionar un finalizador implementando un destructor para la clase. Un destructor de C# automáticamente llama al destructor de su clase base.

Visual C++ también proporciona su propia sintaxis para implementar el Finalize método. Para obtener más información, vea la sección "Destructores y finalizadores" de Cómo: definir y utilizar clases y Structs (CLI de C++).

Dado que la colección de elementos no utilizados es no determinista, no conoce con precisión cuando el recolector de elementos no utilizados realiza finalización. Para liberar recursos inmediatamente, también puede implementar el patrón de dispose y IDisposable (interfaz). El IDisposable.Dispose implementación se puede llamar a los consumidores de la clase para liberar recursos no administrados, y puede utilizar el Finalize método para liberar recursos no administrados en caso de que el Dispose no se llama el método.

Finalize puede realizar casi cualquier acción, como el restablecimiento de un objeto (es decir, hacer que el objeto accesible nuevo) después de que se ha limpiado durante la recolección de elementos no utilizados. Sin embargo, el objeto sólo puede restablecerse una vez; Finalize no puede llamarse en objetos restablecidos durante la recolección de elementos no utilizados. Hay una acción que la implementación de Finalize nunca debe tomar: jamás deberá producir una excepción.

Crear los finalizadores confiables a menudo es difícil, dado que no puede realizar suposiciones sobre el estado de la aplicación así como excepciones no controladas de sistema como OutOfMemoryException y StackOverflowException Finalizar el finalizador. En lugar de implementar un finalizador de la clase para liberar recursos no administrados, puede usar un objeto que se deriva de la System.Runtime.InteropServices.SafeHandle de clase para encapsular los recursos no administrados y, a continuación, implemente el patrón dispose sin un finalizador. .NET Framework proporciona las siguientes clases en el Microsoft.Win32 espacio de nombres que se derivan de System.Runtime.InteropServices.SafeHandle:

En el ejemplo siguiente se usa el patrón de dispose con identificadores seguros en lugar de reemplazar el Finalize método. Define un FileAssociation clase que encapsula la información del registro acerca de la aplicación que trata los archivos con una extensión de archivo determinada. Los identificadores de dos registro devueltos como out parámetros Windows RegOpenKeyEx llamadas de función se pasan a la SafeRegistryHandle constructor. Protegido el tipo Dispose método llama el SafeRegistryHandle.Dispose método para liberar estos dos controladores.

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;

public class FileAssociationInfo : IDisposable
{
   // Private variables.
   private String ext;
   private String openCmd;
   private String args;
   private SafeRegistryHandle hExtHandle, hAppIdHandle;

   // Windows API calls.
   [DllImport("advapi32.dll", CharSet= CharSet.Auto, SetLastError=true)]
   private static extern int RegOpenKeyEx(IntPtr hKey, 
                  String lpSubKey, int ulOptions, int samDesired,
                  out IntPtr phkResult);
   [DllImport("advapi32.dll", CharSet= CharSet.Unicode, EntryPoint = "RegQueryValueExW",
              SetLastError=true)]
   private static extern int RegQueryValueEx(IntPtr hKey,
                  string lpValueName, int lpReserved, out uint lpType, 
                  string lpData, ref uint lpcbData);   
   [DllImport("advapi32.dll", SetLastError = true)]
   private static extern int RegSetValueEx(IntPtr hKey, [MarshalAs(UnmanagedType.LPStr)] string lpValueName,
                  int Reserved, uint dwType, [MarshalAs(UnmanagedType.LPStr)] string lpData,
                  int cpData);
   [DllImport("advapi32.dll", SetLastError=true)]
   private static extern int RegCloseKey(UIntPtr hKey);

   // Windows API constants.
   private const int HKEY_CLASSES_ROOT = unchecked((int) 0x80000000);
   private const int ERROR_SUCCESS = 0;

    private const int KEY_QUERY_VALUE = 1;
    private const int KEY_SET_VALUE = 0x2;

   private const uint REG_SZ = 1;

   private const int MAX_PATH = 260;

   public FileAssociationInfo(String fileExtension)
   {
      int retVal = 0;
      uint lpType = 0;

      if (!fileExtension.StartsWith("."))
             fileExtension = "." + fileExtension;
      ext = fileExtension;

      IntPtr hExtension = IntPtr.Zero;
      // Get the file extension value.
      retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0, KEY_QUERY_VALUE, out hExtension);
      if (retVal != ERROR_SUCCESS) 
         throw new Win32Exception(retVal);
      // Instantiate the first SafeRegistryHandle.
      hExtHandle = new SafeRegistryHandle(hExtension, true);

      string appId = new string(' ', MAX_PATH);
      uint appIdLength = (uint) appId.Length;
      retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, 0, out lpType, appId, ref appIdLength);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);
      // We no longer need the hExtension handle.
      hExtHandle.Dispose();

      // Determine the number of characters without the terminating null.
      appId = appId.Substring(0, (int) appIdLength / 2 - 1) + @"\shell\open\Command";

      // Open the application identifier key.
      string exeName = new string(' ', MAX_PATH);
      uint exeNameLength = (uint) exeName.Length;
      IntPtr hAppId;
      retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), appId, 0, KEY_QUERY_VALUE | KEY_SET_VALUE,
                            out hAppId);
       if (retVal != ERROR_SUCCESS) 
         throw new Win32Exception(retVal);

      // Instantiate the second SafeRegistryHandle.
      hAppIdHandle = new SafeRegistryHandle(hAppId, true);

      // Get the executable name for this file type.
      string exePath = new string(' ', MAX_PATH);
      uint exePathLength = (uint) exePath.Length;
      retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, out lpType, exePath, ref exePathLength);
      if (retVal != ERROR_SUCCESS)
         throw new Win32Exception(retVal);

      // Determine the number of characters without the terminating null.
      exePath = exePath.Substring(0, (int) exePathLength / 2 - 1);
      // Remove any environment strings.
      exePath = Environment.ExpandEnvironmentVariables(exePath);

      int position = exePath.IndexOf('%');
      if (position >= 0) {
         args = exePath.Substring(position);
         // Remove command line parameters ('%0', etc.).
         exePath = exePath.Substring(0, position).Trim();
      }
      openCmd = exePath;   
   }

   public String Extension
   { get { return ext; } }

   public String Open
   { get { return openCmd; } 
     set {
        if (hAppIdHandle.IsInvalid | hAppIdHandle.IsClosed)
           throw new InvalidOperationException("Cannot write to registry key."); 
        if (! File.Exists(value)) {
           string message = String.Format("'{0}' does not exist", value);
           throw new FileNotFoundException(message); 
        }
        string cmd = value + " %1";
        int retVal = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, 
                                   REG_SZ, value, value.Length + 1);
        if (retVal != ERROR_SUCCESS)
           throw new Win32Exception(retVal);                          
     } }

   public void Dispose() 
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }   

   protected void Dispose(bool disposing)
   {
      // Ordinarily, we release unmanaged resources here; 
      // but all are wrapped by safe handles.

      // Release disposable objects.
      if (disposing) {
         if (hExtHandle != null) hExtHandle.Dispose();
         if (hAppIdHandle != null) hAppIdHandle.Dispose();
      }
   }
}

En el ejemplo siguiente se comprueba que el Finalize se invoca cuando un objeto que reemplaza Finalize se destruye. Tenga en cuenta que, en una aplicación de producción, el Finalize se reemplazaría el método para liberar recursos no administrados mantenidos por el objeto. Tenga en cuenta también que el ejemplo de C# proporciona un destructor en lugar de reemplazar el Finalize método.

using System;
using System.Diagnostics;

public class ExampleClass
{
   Stopwatch sw;

   public ExampleClass()
   {
      sw = Stopwatch.StartNew();
      Console.WriteLine("Instantiated object");
   } 

   public void ShowDuration()
   {
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        this, sw.Elapsed);
   }

   ~ExampleClass()
   {
      Console.WriteLine("Finalizing object");
      sw.Stop();
      Console.WriteLine("This instance of {0} has been in existence for {1}",
                        this, sw.Elapsed);
   }
}

public class Demo
{
   public static void Main()
   {
      ExampleClass ex = new ExampleClass();
      ex.ShowDuration();
   }
}
// The example displays output like the following:
//    Instantiated object
//    This instance of ExampleClass has been in existence for 00:00:00.0011060
//    Finalizing object
//    This instance of ExampleClass has been in existence for 00:00:00.0036294

For an additional example that overrides the M:System.Object.Finalize method, see the M:System.GC.SuppressFinalize(System.Object) method.

Plataforma universal de Windows
Disponible desde 8
.NET Framework
Disponible desde 1.1
Biblioteca de clases portable
Se admite en: plataformas portátiles de .NET
Silverlight
Disponible desde 2.0
Windows Phone Silverlight
Disponible desde 7.0
Windows Phone
Disponible desde 8.1
Volver al principio
Mostrar: