Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.
Metoden Finalize används för att utföra rensningsåtgärder på ohanterade resurser som innehas av det aktuella objektet innan objektet förstörs. Metoden är skyddad och är därför endast tillgänglig via den här klassen eller via en härledd klass.
Så här fungerar slutförande
Klassen Object tillhandahåller ingen implementering för Finalize metoden och skräpinsamlaren markerar inte typer som härletts från Object för slutförande om de inte åsidosätter Finalize metoden.
Om en typ åsidosätter Finalize metoden lägger sopinsamlaren till en post för varje instans av typen i ett internt system som kallas finalizeringskö. Finaliseringskön innehåller poster för alla objekt i den hanterade heapen vars finaliseringskod måste köras innan skräpinsamlaren kan frigöra sitt minne. Skräpinsamlaren anropar Finalize sedan metoden automatiskt under följande villkor:
- När skräpinsamlaren har upptäckt att ett objekt är otillgängligt, såvida inte objektet har undantagits från finalisering genom ett anrop till GC.SuppressFinalize-metoden.
- Endast på .NET Framework, under avstängning av en programdomän, såvida inte objektet är undantaget från slutförande. Under avstängningen slutförs även objekt som fortfarande är tillgängliga.
Finalize anropas automatiskt bara en gång på en viss instans, såvida inte objektet registreras på nytt med hjälp av en mekanism som GC.ReRegisterForFinalize och GC.SuppressFinalize metoden inte har anropats senare.
Finalize åtgärder har följande begränsningar:
- Den exakta tiden när finalizern körs är odefinierad. För att säkerställa deterministisk frisläppning av resurser för instanser av din klass implementerar du en
Closemetod eller tillhandahåller en IDisposable.Dispose implementering. - Finalizerarna för de två objekten är inte garanterade att köras i någon specifik ordning, även om det ena objektet refererar till det andra. Om objekt A har en referens till Objekt B och båda har slutförare kan objekt B redan ha slutförts när slutföraren av objekt A startar.
- Tråden som finalizern körs på är ospecificerad.
Metoden Finalize kanske inte körs till slutförande eller kanske inte körs alls under följande exceptionella omständigheter:
- Om en annan finalizer blockerar på obestämd tid (går in i en oändlig loop, försöker få ett lås som det aldrig kan få, och så vidare). Eftersom körningsmiljön försöker avsluta finalizers kanske andra finalizers inte anropas om en finalizer blockerar utan tidsgräns.
- Om processen avslutas utan att ge körtiden en chans att städa upp. I det här fallet är det första meddelandet från körmiljön om att processen avslutas ett DLL_PROCESS_DETACH-meddelande.
Körtiden fortsätter att genomföra slutbearbetning av objekt under avslutningen endast medan antalet finaliserbara objekt fortsätter att minska.
Om Finalize eller en åsidosättning av Finalize utlöser ett undantag och om körtiden inte hanteras av ett program som åsidosätter standardprincipen, så avslutar körtiden processen och inga aktiva try/finally-block eller finalizer körs. Det här beteendet säkerställer processintegriteten om slutföraren inte kan frigöra eller förstöra resurser.
Åsidosätta metoden Slutför
Du bör åsidosätta Finalize för en klass som använder ohanterade resurser, till exempel filhandtag eller databasanslutningar som måste släppas när det hanterade objekt som använder dem tas bort under skräpinsamlingen. Du bör inte implementera en Finalize metod för hanterade objekt eftersom skräpinsamlaren släpper hanterade resurser automatiskt.
Viktigt!
Om ett SafeHandle-objekt är tillgängligt som omsluter din ohanterade resurs är det rekommenderade alternativet att implementera dispose-mönstret med ett säkert handtag och inte åsidosätta Finalize. Mer information finns i avsnittet Om alternativet SafeHandle .
Metoden Object.Finalize gör ingenting som standard, men du bör endast åsidosätta Finalize om det behövs och endast för att frigöra ohanterade resurser. Det brukar ta mycket längre tid att frigöra minne om en finaliseringsoperation körs, eftersom det krävs minst två soptömningar. Dessutom bör du endast åsidosätta Finalize metoden för referenstyper. Common Language Runtime (CLR) avslutar endast referenstyper. Den ignorerar finalizers på värdetyper.
Metodens Object.Finalize omfång är protected. Du bör behålla det här begränsade omfånget när du åsidosätter metoden i din klass. Genom att skydda en Finalize metod hindrar du användare av ditt program från att anropa ett objekts Finalize -metod direkt.
Varje implementering av Finalize i en härledd typ måste anropa bastypens implementering av Finalize. Det här är det enda fallet där programkod tillåts anropa Finalize. Ett objekts Finalize -metod ska inte anropa en metod för andra objekt än dess basklass. Det beror på att de andra objekten som anropas kan samlas in samtidigt som det anropande objektet, till exempel vid en avstängning av Common Language Runtime.
Anmärkning
C#-kompilatorn tillåter inte att du åsidosätter Finalize metoden. I stället tillhandahåller du en slutförare genom att implementera en destruktor för din klass. En C#-destruktor anropar automatiskt destruktorn för basklassen.
Visual C++ innehåller också en egen syntax för att implementera Finalize metoden. Mer information finns i avsnittet "Destructors and finalizers" i How to: Define and Consume Classes and Structs (C++/CLI).
Eftersom skräpinsamlingen är icke-deterministisk vet du inte exakt när skräpinsamlaren slutför. Om du vill frigöra resurser omedelbart kan du också välja att implementera dispose-mönster och gränssnittet IDisposable. Implementeringen IDisposable.Dispose kan anropas av användare av klassen för att frigöra ohanterade resurser, och du kan använda Finalize metoden för att frigöra ohanterade resurser om Dispose metoden inte anropas.
Finalize kan vidta nästan vilken åtgärd som helst, inklusive att återuppväcka ett objekt (dvs. göra objektet tillgängligt igen) efter att det har rensats under skräpinsamlingen. Objektet kan dock bara återuppstå en gång. Finalize kan inte anropas på uppståndna objekt under skräpinsamlingen.
Alternativet SafeHandle
Det är ofta svårt att skapa tillförlitliga finalizers eftersom du inte kan göra antaganden om programmets tillstånd och eftersom ohanterade systemfel som OutOfMemoryException och StackOverflowException avslutar slutprogrammet. I stället för att implementera en finalator för klassen för att frigöra ohanterade resurser kan du använda ett objekt som härleds från System.Runtime.InteropServices.SafeHandle klassen för att omsluta dina ohanterade resurser och sedan implementera mönstret för bortskaffande utan en finalizer. .NET Framework tillhandahåller följande klasser i namnområdet Microsoft.Win32 som härleds från System.Runtime.InteropServices.SafeHandle:
- SafeFileHandle är en omslutningsklass för ett filhandtag.
- SafeMemoryMappedFileHandle är en omslutningsklass för minnesmappade filhandtag.
- SafeMemoryMappedViewHandle är en omslutningsklass för en pekare till ett block med ohanterat minne.
- SafeNCryptKeyHandle, SafeNCryptProviderHandleoch SafeNCryptSecretHandle är omslutningsklasser för kryptografiska handtag.
- SafePipeHandle är en omslutningsklass för rörhandtag.
- SafeRegistryHandle är en omslutningsklass för ett handtag till en registernyckel.
- SafeWaitHandle är en omslutningsklass för ett väntehandtag.
I följande exempel används dispose-mönstret med säkra handtag i stället för att Finalize åsidosätta metoden. Den definierar en FileAssociation klass som omsluter registerinformation om programmet som hanterar filer med ett visst filnamnstillägg. De två registerreferenserna som returneras som out parametrar av Windows RegOpenKeyEx-funktionsanrop skickas till SafeRegistryHandle konstruktorn. Typens skyddade Dispose-metod anropar sedan SafeRegistryHandle.Dispose-metoden som frigör dessa två referenser.
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(disposing: 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();
}
}
}
open Microsoft.Win32.SafeHandles
open System
open System.ComponentModel
open System.IO
open System.Runtime.InteropServices
// Windows API constants.
let HKEY_CLASSES_ROOT = 0x80000000
let ERROR_SUCCESS = 0
let KEY_QUERY_VALUE = 1
let KEY_SET_VALUE = 0x2
let REG_SZ = 1u
let MAX_PATH = 260
// Windows API calls.
[<DllImport("advapi32.dll", CharSet= CharSet.Auto, SetLastError=true)>]
extern int RegOpenKeyEx(nativeint hKey, string lpSubKey, int ulOptions, int samDesired, nativeint& phkResult)
[<DllImport("advapi32.dll", CharSet= CharSet.Unicode, EntryPoint = "RegQueryValueExW", SetLastError=true)>]
extern int RegQueryValueEx(nativeint hKey, string lpValueName, int lpReserved, uint& lpType, string lpData, uint& lpcbData)
[<DllImport("advapi32.dll", SetLastError = true)>]
extern int RegSetValueEx(nativeint hKey, [<MarshalAs(UnmanagedType.LPStr)>] string lpValueName, int Reserved, uint dwType, [<MarshalAs(UnmanagedType.LPStr)>] string lpData, int cpData)
[<DllImport("advapi32.dll", SetLastError=true)>]
extern int RegCloseKey(unativeint hKey)
type FileAssociationInfo(fileExtension: string) =
// Private values.
let ext =
if fileExtension.StartsWith "." |> not then
"." + fileExtension
else
fileExtension
let mutable args = ""
let mutable hAppIdHandle = Unchecked.defaultof<SafeRegistryHandle>
let mutable hExtHandle = Unchecked.defaultof<SafeRegistryHandle>
let openCmd =
let mutable lpType = 0u
let mutable hExtension = 0n
// Get the file extension value.
let retVal = RegOpenKeyEx(nativeint HKEY_CLASSES_ROOT, fileExtension, 0, KEY_QUERY_VALUE, &hExtension)
if retVal <> ERROR_SUCCESS then
raise (Win32Exception retVal)
// Instantiate the first SafeRegistryHandle.
hExtHandle <- new SafeRegistryHandle(hExtension, true)
let appId = String(' ', MAX_PATH)
let mutable appIdLength = uint appId.Length
let retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, 0, &lpType, appId, &appIdLength)
if retVal <> ERROR_SUCCESS then
raise (Win32Exception retVal)
// We no longer need the hExtension handle.
hExtHandle.Dispose()
// Determine the number of characters without the terminating null.
let appId = appId.Substring(0, int appIdLength / 2 - 1) + @"\shell\open\Command"
// Open the application identifier key.
let exeName = String(' ', MAX_PATH)
let exeNameLength = uint exeName.Length
let mutable hAppId = 0n
let retVal = RegOpenKeyEx(nativeint HKEY_CLASSES_ROOT, appId, 0, KEY_QUERY_VALUE ||| KEY_SET_VALUE, &hAppId)
if retVal <> ERROR_SUCCESS then
raise (Win32Exception retVal)
// Instantiate the second SafeRegistryHandle.
hAppIdHandle <- new SafeRegistryHandle(hAppId, true)
// Get the executable name for this file type.
let exePath = String(' ', MAX_PATH)
let mutable exePathLength = uint exePath.Length
let retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, &lpType, exePath, &exePathLength)
if retVal <> ERROR_SUCCESS then
raise (Win32Exception retVal)
// Determine the number of characters without the terminating null.
let exePath =
exePath.Substring(0, int exePathLength / 2 - 1)
// Remove any environment strings.
|> Environment.ExpandEnvironmentVariables
let position = exePath.IndexOf '%'
if position >= 0 then
args <- exePath.Substring position
// Remove command line parameters ('%0', etc.).
exePath.Substring(0, position).Trim()
else
exePath
member _.Extension =
ext
member _.Open
with get () = openCmd
and set (value) =
if hAppIdHandle.IsInvalid || hAppIdHandle.IsClosed then
raise (InvalidOperationException "Cannot write to registry key.")
if not (File.Exists value) then
raise (FileNotFoundException $"'{value}' does not exist")
let cmd = value + " %1"
let retVal = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0, REG_SZ, value, value.Length + 1)
if retVal <> ERROR_SUCCESS then
raise (Win32Exception retVal)
member this.Dispose() =
this.Dispose true
GC.SuppressFinalize this
member _.Dispose(disposing) =
// Ordinarily, we release unmanaged resources here
// but all are wrapped by safe handles.
// Release disposable objects.
if disposing then
if hExtHandle <> null then hExtHandle.Dispose()
if hAppIdHandle <> null then hAppIdHandle.Dispose()
interface IDisposable with
member this.Dispose() =
this.Dispose()
Imports Microsoft.Win32.SafeHandles
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text
Public Class FileAssociationInfo : Implements IDisposable
' Private variables.
Private ext As String
Private openCmd As String
Private args As String
Private hExtHandle, hAppIdHandle As SafeRegistryHandle
' Windows API calls.
Private Declare Unicode Function RegOpenKeyEx Lib"advapi32.dll" _
Alias "RegOpenKeyExW" (hKey As IntPtr, lpSubKey As String, _
ulOptions As Integer, samDesired As Integer, _
ByRef phkResult As IntPtr) As Integer
Private Declare Unicode Function RegQueryValueEx Lib "advapi32.dll" _
Alias "RegQueryValueExW" (hKey As IntPtr, _
lpValueName As String, lpReserved As Integer, _
ByRef lpType As UInteger, lpData As String, _
ByRef lpcbData As UInteger) As Integer
Private Declare Function RegSetValueEx Lib "advapi32.dll" _
(hKey As IntPtr, _
<MarshalAs(UnmanagedType.LPStr)> lpValueName As String, _
reserved As Integer, dwType As UInteger, _
<MarshalAs(UnmanagedType.LPStr)> lpData As String, _
cpData As Integer) As Integer
Private Declare Function RegCloseKey Lib "advapi32.dll" _
(hKey As IntPtr) As Integer
' Windows API constants.
Private Const HKEY_CLASSES_ROOT As Integer = &h80000000
Private Const ERROR_SUCCESS As Integer = 0
Private Const KEY_QUERY_VALUE As Integer = 1
Private Const KEY_SET_VALUE As Integer = &h2
Private REG_SZ As UInteger = 1
Private Const MAX_PATH As Integer = 260
Public Sub New(fileExtension As String)
Dim retVal As Integer = 0
Dim lpType As UInteger = 0
If Not fileExtension.StartsWith(".") Then
fileExtension = "." + fileExtension
End If
ext = fileExtension
Dim hExtension As IntPtr = IntPtr.Zero
' Get the file extension value.
retVal = RegOpenKeyEx(New IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0,
KEY_QUERY_VALUE, hExtension)
if retVal <> ERROR_SUCCESS Then
Throw New Win32Exception(retVal)
End If
' Instantiate the first SafeRegistryHandle.
hExtHandle = New SafeRegistryHandle(hExtension, True)
Dim appId As New String(" "c, MAX_PATH)
Dim appIdLength As UInteger = CUInt(appId.Length)
retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(), String.Empty, _
0, lpType, appId, appIdLength)
if retVal <> ERROR_SUCCESS Then
Throw New Win32Exception(retVal)
End If
' We no longer need the hExtension handle.
hExtHandle.Dispose()
' Determine the number of characters without the terminating null.
appId = appId.Substring(0, CInt(appIdLength) \ 2 - 1) + "\shell\open\Command"
' Open the application identifier key.
Dim exeName As New string(" "c, MAX_PATH)
Dim exeNameLength As UInteger = CUInt(exeName.Length)
Dim hAppId As IntPtr
retVal = RegOpenKeyEx(New IntPtr(HKEY_CLASSES_ROOT), appId, 0,
KEY_QUERY_VALUE Or KEY_SET_VALUE, hAppId)
If retVal <> ERROR_SUCCESS Then
Throw New Win32Exception(retVal)
End If
' Instantiate the second SafeRegistryHandle.
hAppIdHandle = New SafeRegistryHandle(hAppId, True)
' Get the executable name for this file type.
Dim exePath As New string(" "c, MAX_PATH)
Dim exePathLength As UInteger = CUInt(exePath.Length)
retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(), _
String.Empty, 0, lpType, exePath, exePathLength)
If retVal <> ERROR_SUCCESS Then
Throw New Win32Exception(retVal)
End If
' Determine the number of characters without the terminating null.
exePath = exePath.Substring(0, CInt(exePathLength) \ 2 - 1)
exePath = Environment.ExpandEnvironmentVariables(exePath)
Dim position As Integer = exePath.IndexOf("%"c)
If position >= 0 Then
args = exePath.Substring(position)
' Remove command line parameters ('%0', etc.).
exePath = exePath.Substring(0, position).Trim()
End If
openCmd = exePath
End Sub
Public ReadOnly Property Extension As String
Get
Return ext
End Get
End Property
Public Property Open As String
Get
Return openCmd
End Get
Set
If hAppIdHandle.IsInvalid Or hAppIdHandle.IsClosed Then
Throw New InvalidOperationException("Cannot write to registry key.")
End If
If Not File.Exists(value) Then
Dim message As String = String.Format("'{0}' does not exist", value)
Throw New FileNotFoundException(message)
End If
Dim cmd As String = value + " %1"
Dim retVal As Integer = RegSetValueEx(hAppIdHandle.DangerousGetHandle(), String.Empty, 0,
REG_SZ, value, value.Length + 1)
If retVal <> ERROR_SUCCESS Then
Throw New Win32Exception(retVal)
End If
End Set
End Property
Public Sub Dispose() _
Implements IDisposable.Dispose
Dispose(disposing:=True)
GC.SuppressFinalize(Me)
End Sub
Protected Sub Dispose(disposing As Boolean)
' Ordinarily, we release unmanaged resources here
' but all are wrapped by safe handles.
' Release disposable objects.
If disposing Then
If hExtHandle IsNot Nothing Then hExtHandle.Dispose()
If hAppIdHandle IsNot Nothing Then hAppIdHandle.Dispose()
End If
End Sub
End Class