Az IDisposable-t implementáló objektumok használata
A közös nyelvi futtatókörnyezet szemétgyűjtője (GC) visszanyeri a felügyelt objektumok által használt memóriát. A nem felügyelt erőforrásokat használó típusok általában implementálják a IDisposable nem felügyelt erőforrások visszanyeréséhez szükséges felületet vagy IAsyncDisposable felületet. Amikor befejezi egy implementálást IDisposablevégző objektum használatát, meghívja Dispose az objektumot vagy DisposeAsync az implementációt, hogy explicit módon végezze el a törlést. Ezt a következő két módszer egyikével teheti meg:
- A C#
using
utasítással vagy deklarációval (Using
a Visual Basicben). - Egy blokk implementálásával
try/finally
és a Dispose metódus meghívásával DisposeAsync afinally
.
Fontos
A GC nem dobja el az objektumokat, mivel nincs ismerete vagy .IDisposable.Dispose()IAsyncDisposable.DisposeAsync() A csoportházirend-objektum csak azt tudja, hogy egy objektum véglegesíthető-e (vagyis meghatároz egy metódust Object.Finalize() ), és mikor kell meghívni az objektum véglegesítőjét. További információ: A véglegesítés működése. A megvalósítással Dispose
DisposeAsync
kapcsolatos további részletekért lásd:
Azokat az objektumokat, amelyek implementálják System.IDisposable vagy System.IAsyncDisposable mindig megfelelően kell megsemmisíteni, függetlenül a változók hatókörétől, kivéve, ha másként kifejezetten meg van állítva. Azok a típusok, amelyek véglegesítőt határoznak meg a nem felügyelt erőforrások kiadásához, általában a saját vagy DisposeAsync
az implementációjukból érkező hívásokat használják.GC.SuppressFinalizeDispose
A hívás SuppressFinalize azt jelzi a csoportházirend-objektumnak, hogy a véglegesítő már lefutott, és az objektumot nem szabad előléptetni a véglegesítéshez.
A using utasítás
A using
C# utasítása és a Using
Visual Basic utasítása leegyszerűsíti az objektum törléséhez írandó kódot. Az using
utasítás lekért egy vagy több erőforrást, végrehajtja a megadott utasításokat, és automatikusan megsemmisíti az objektumot. Az using
utasítás azonban csak a metódus hatókörén belül használt objektumok esetében hasznos.
Az alábbi példa az using
utasítással hoz létre és szabadít fel egy objektumot System.IO.StreamReader .
using System.IO;
class UsingStatement
{
static void Main()
{
var buffer = new char[50];
using (StreamReader streamReader = new("file1.txt"))
{
int charsRead = 0;
while (streamReader.Peek() != -1)
{
charsRead = streamReader.Read(buffer, 0, buffer.Length);
//
// Process characters read.
//
}
}
}
}
Imports System.IO
Module UsingStatement
Public Sub Main()
Dim buffer(49) As Char
Using streamReader As New StreamReader("File1.txt")
Dim charsRead As Integer
Do While streamReader.Peek() <> -1
charsRead = streamReader.Read(buffer, 0, buffer.Length)
'
' Process characters read.
'
Loop
End Using
End Sub
End Module
A using
deklaráció egy alternatív szintaxis, amely a zárójelek eltávolítására használható, a hatókörkezelés pedig implicit.
using System.IO;
class UsingDeclaration
{
static void Main()
{
var buffer = new char[50];
using StreamReader streamReader = new("file1.txt");
int charsRead = 0;
while (streamReader.Peek() != -1)
{
charsRead = streamReader.Read(buffer, 0, buffer.Length);
//
// Process characters read.
//
}
}
}
Bár az StreamReader osztály implementálja az IDisposable interfészt, amely azt jelzi, hogy nem felügyelt erőforrást használ, a példa nem hívja meg explicit módon a metódust StreamReader.Dispose . Amikor a C# vagy a Visual Basic fordító találkozik az using
utasítással, olyan köztes nyelvet (IL) bocsát ki, amely egyenértékű az alábbi kóddal, amely kifejezetten tartalmaz egy blokkot try/finally
.
using System.IO;
class TryFinallyGenerated
{
static void Main()
{
var buffer = new char[50];
StreamReader? streamReader = null;
try
{
streamReader = new StreamReader("file1.txt");
int charsRead = 0;
while (streamReader.Peek() != -1)
{
charsRead = streamReader.Read(buffer, 0, buffer.Length);
//
// Process characters read.
//
}
}
finally
{
// If non-null, call the object's Dispose method.
streamReader?.Dispose();
}
}
}
Imports System.IO
Module TryFinallyGenerated
Public Sub Main()
Dim buffer(49) As Char
Dim streamReader As New StreamReader("File1.txt")
Try
Dim charsRead As Integer
Do While streamReader.Peek() <> -1
charsRead = streamReader.Read(buffer, 0, buffer.Length)
'
' Process characters read.
'
Loop
Finally
If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
End Try
End Sub
End Module
A C# using
utasítással több erőforrást is beszerezhet egyetlen utasításban, ami belsőleg egyenértékű a beágyazott using
utasításokkal. Az alábbi példa két StreamReader objektumot példányosít két különböző fájl tartalmának olvasásához.
using System.IO;
class SingleStatementMultiple
{
static void Main()
{
var buffer1 = new char[50];
var buffer2 = new char[50];
using StreamReader version1 = new("file1.txt"),
version2 = new("file2.txt");
int charsRead1, charsRead2 = 0;
while (version1.Peek() != -1 && version2.Peek() != -1)
{
charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
//
// Process characters read.
//
}
}
}
Kipróbálás/végül letiltás
A blokkok utasításban using
való körbefuttatása try/finally
helyett dönthet úgy, hogy közvetlenül implementálja a try/finally
blokkot. Ez lehet a személyes kódolási stílusa, vagy az alábbi okok valamelyike miatt érdemes ezt megtennie:
- Blokk hozzáadása
catch
a blokkbantry
szereplő kivételek kezeléséhez. Ellenkező esetben azusing
utasításban szereplő kivételeket a rendszer nem kezelte. - Olyan objektum példányosítása, amelynek IDisposable hatóköre nem helyi ahhoz a blokkhoz, amelyen belül deklarálva van.
Az alábbi példa az előző példához hasonló, azzal a kivétellel, hogy blokkot használ egy try/catch/finally
StreamReader objektum példányosítására, használatára és megsemmisítésére, valamint a StreamReader konstruktor és a ReadToEnd metódus által alkalmazott kivételek kezelésére. A blokkban lévő finally
kód ellenőrzi, hogy a implementálható IDisposable objektum nem null
a metódus meghívása Dispose előtt van-e. Ennek elmulasztása futásidőben kivételt NullReferenceException eredményezhet.
using System;
using System.Globalization;
using System.IO;
class TryExplicitCatchFinally
{
static void Main()
{
StreamReader? streamReader = null;
try
{
streamReader = new StreamReader("file1.txt");
string contents = streamReader.ReadToEnd();
var info = new StringInfo(contents);
Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
}
catch (FileNotFoundException)
{
Console.WriteLine("The file cannot be found.");
}
catch (IOException)
{
Console.WriteLine("An I/O error has occurred.");
}
catch (OutOfMemoryException)
{
Console.WriteLine("There is insufficient memory to read the file.");
}
finally
{
streamReader?.Dispose();
}
}
}
Imports System.Globalization
Imports System.IO
Module TryExplicitCatchFinally
Sub Main()
Dim streamReader As StreamReader = Nothing
Try
streamReader = New StreamReader("file1.txt")
Dim contents As String = streamReader.ReadToEnd()
Dim info As StringInfo = New StringInfo(contents)
Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
Catch e As FileNotFoundException
Console.WriteLine("The file cannot be found.")
Catch e As IOException
Console.WriteLine("An I/O error has occurred.")
Catch e As OutOfMemoryException
Console.WriteLine("There is insufficient memory to read the file.")
Finally
If streamReader IsNot Nothing Then streamReader.Dispose()
End Try
End Sub
End Module
Ezt az alapszintű mintát követheti, ha egy blokk implementálása vagy implementálása try/finally
mellett dönt, mert a programozási nyelv nem támogatja az utasítást using
, de lehetővé teszi a Dispose metódus közvetlen hívását.
IDisposable-példányok tagjai
Ha egy osztály egy példánymező vagy tulajdonság tulajdonosa, és a típusa implementálva IDisposablevan, az osztálynak is implementálnia IDisposablekell. További információ: Kaszkádolt megsemmisítés implementálása.