Implementace metody Dispose

Implementace Dispose metody je primárně určena pro uvolňování nespravovaných prostředků. Při práci se členy instance, které jsou IDisposable implementace, je běžné volání kaskády Dispose . K dispozici jsou další důvody pro implementaci Dispose , například k uvolnění přidělené paměti, odebrání položky, která byla přidána do kolekce, nebo k signalizaci vydání zámku, který byl získán.

Systém uvolňování paměti .NET nepřiřazuje ani neuvolní nespravovanou paměť. Vzor pro likvidaci objektu, který je označován jako vzor Dispose, ukládá pořadí pro životnost objektu. Vzor Dispose se používá pro objekty, které implementují IDisposable rozhraní a je běžné při interakci s popisovači souborů a kanálů, obslužnými rutinami registru, obslužnými rutinami čekání nebo ukazateli na bloky nespravované paměti. Důvodem je to, že systém uvolňování paměti nemůže uvolnit nespravované objekty.

Aby bylo možné zajistit, aby se prostředky vždy vyčistily správně, Dispose měla by být metoda idempotentní, aby byla vícenásobně volat bez vyvolání výjimky. Kromě toho by následné vyvolání Dispose nemělo dělat nic.

Příklad kódu, který je k dispozici pro GC.KeepAlive metodu, ukazuje, jak uvolňování paměti může způsobit spuštění finalizační metody, zatímco nespravovaný odkaz na objekt nebo jeho členy se stále používá. Může být vhodné využít GC.KeepAlive k tomu, aby objekt nezpůsobil pro uvolňování paměti od začátku aktuální rutiny do bodu, ve kterém je tato metoda volána.

Tip

V souvislosti s vkládáním závislostí při registraci služeb v nástroji IServiceCollection je Doba života služby implicitně spravovaná vaším jménem. IServiceProviderA odpovídající IHost čištění prostředků orchestrace. Konkrétně implementace IDisposable a IAsyncDisposable jsou správně odstraněny na konci zadané doby života.

Další informace najdete v tématu Injektáže závislostí v rozhraní .NET.

Sejf obslužné rutiny

Psaní kódu pro finalizační metodu objektu je složitý úkol, který může způsobit problémy, není-li prováděn správně. Proto doporučujeme System.Runtime.InteropServices.SafeHandle místo implementace finalizační metody vytvořit objekty.

System.Runtime.InteropServices.SafeHandleJe abstraktní spravovaný typ, který zabalí System.IntPtr , který identifikuje nespravovaný prostředek. V Windows může identifikovat popisovač v systému UNIX a popisovač souboru. Poskytuje veškerou logiku nutnou k zajištění toho, že se tento prostředek vydává jednou a jenom jednou, když SafeHandle je vyřazený z nebo když jsou všechny odkazy na SafeHandle vyřazené a SafeHandle instance se dokončuje.

System.Runtime.InteropServices.SafeHandleJe abstraktní základní třída. Odvozené třídy poskytují konkrétní instance pro různé druhy popisovačů. Tyto odvozené třídy ověřují, které hodnoty pro System.IntPtr jsou považovány za neplatné a jak je ve skutečnosti zadarmo zadarmo. Například SafeFileHandle je odvozen od SafeHandle pro zabalení IntPtrs , které identifikuje otevřené popisovače souborů/popisovače, a Přepisuje její SafeHandle.ReleaseHandle() metodu pro její zavření (prostřednictvím close funkce v systému UNIX nebo CloseHandle funkce na Windows). Většina rozhraní API v knihovnách .NET, které vytváří nespravovaný prostředek, zabalí ho do SafeHandle a a vrátí, SafeHandle co je potřeba, místo ručního vrácení nezpracovaného ukazatele zpět. V situacích, kdy pracujete s nespravovanou komponentou a získáte IntPtr pro nespravovaný prostředek, můžete vytvořit vlastní SafeHandle typ, který ho zabalí. V důsledku toho některé SafeHandle netypy nemusejí implementovat finalizační metody; většina implementací vzorku na jedno použití pouze ukončí zalamování jiných spravovaných prostředků, z nichž některé mohou být SafeHandle s.

Následující odvozené třídy v Microsoft.Win32.SafeHandles oboru názvů poskytují bezpečné popisovače:

Dispose () a Dispose (bool)

IDisposableRozhraní vyžaduje implementaci jediné metody bez parametrů, Dispose . Kromě toho by měla být jakákoli nezapečetěná Třída mít Dispose(bool) k implementaci další metodu přetížení:

  • publicnevirtuální ( NonInheritable v Visual Basic) IDisposable.Dispose implementace bez parametrů.

  • protected virtualmetoda ( Overridable in Visual Basic), Dispose jejíž signatura je:

    protected virtual void Dispose(bool disposing)
    {
    }
    
    Protected Overridable Sub Dispose(disposing As Boolean)
    End Sub
    

    Důležité

    disposingParametr by měl být false při volání z finalizační true metody a při volání z IDisposable.Dispose metody. Jinými slovy, je true při deterministickém volání a false při nedeterministickém volání.

Metoda Dispose ()

vzhledem k tomu, že public nevirtuální ( NonInheritable v Visual Basic), metoda bez parametrů Dispose je volána příjemcem typu, jeho účelem je uvolnit nespravované prostředky, provést obecné vyčištění a označit, že finalizační metoda, pokud je k dispozici, nemusí být spuštěna. Uvolnění aktuální paměti přidružené ke spravovanému objektu je vždy doména uvolňování paměti. Z tohoto důvodu má standardní implementaci:

public void Dispose()
{
    // Dispose of unmanaged resources.
    Dispose(true);
    // Suppress finalization.
    GC.SuppressFinalize(this);
}
Public Sub Dispose() _
    Implements IDisposable.Dispose
    ' Dispose of unmanaged resources.
    Dispose(True)
    ' Suppress finalization.
    GC.SuppressFinalize(Me)
End Sub

DisposeMetoda provádí vyčištění všech objektů, takže systém uvolňování paměti již nemusí volat Object.Finalize přepis objektů. Proto volání SuppressFinalize metody zabrání systému uvolňování paměti ve spuštění finalizační metody. Pokud typ nemá finalizační metodu, volání GC.SuppressFinalize nemá žádný vliv. Všimněte si, že vlastní vyčištění je provedeno Dispose(bool) přetížením metody.

Přetížení metody Dispose (bool)

V přetížení disposing je parametr Boolean , který označuje, zda volání metody pochází z Dispose metody (její hodnota je true ) nebo z finalizační metody (její hodnota je false ).

Tělo metody sestává ze dvou bloků kódu:

  • Blok, který uvolní nespravované prostředky. Tento blok se provede bez ohledu na hodnotu disposing parametru.

  • Podmíněný blok, který uvolní spravované prostředky. Tento blok disposing se spustí, pokud je hodnota true . Mezi uvolněné spravované prostředky patří:

    • Spravované objekty, které implementují IDisposable . Podmíněný blok lze použít k volání jejich Dispose implementace (Dispose kaskády). Pokud jste použili odvozenou třídu System.Runtime.InteropServices.SafeHandle pro zabalení nespravovaného prostředku, měli byste zavolat SafeHandle.Dispose() implementaci zde.

    • Spravované objekty, které využívají velké množství paměti nebo využívají omezených prostředky. Přiřazení rozsáhlých odkazů na spravované objekty, aby null bylo pravděpodobnější, že budou pravděpodobně nedostupné. Tím je vydáváte rychleji než v případě, že byly uvolněny nedeterministické, což je obvykle provedeno mimo podmíněný blok.

Pokud volání metody pochází z finalizační metody, měla by být spuštěna pouze kód, který uvolní nespravované prostředky. Implementátor zodpovídá za to, že tato falešná cesta nekomunikuje se spravovanými objekty, které mohou být uvolněny. To je důležité kvůli tomu, že systém uvolňování paměti během finalizace nedeterministické spravované objekty.

Volání Dispose pro kaskády

Pokud vaše třída vlastní pole nebo vlastnost a její typ implementuje IDisposable , měla by být také implementována obsažená třída IDisposable . Třída, která vytváří instanci IDisposable implementace a ukládá ji jako člen instance, je také zodpovědná za její vyčištění. To vám umožní zajistit, aby se odkazované typy na jedno použití dostaly do čistého výkonu prostřednictvím Dispose metody. V tomto příkladu je třída sealed (nebo NotInheritable v Visual Basic).

using System;

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose()
    {
        _bar?.Dispose();
    }
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

Implementace vzoru Dispose

všechny nezapečetěné třídy nebo (Visual Basic třídy neupravené jako NotInheritable ) by měly být považovány za potenciální základní třídu, protože by mohly být děděny. Pokud implementujete vzor Dispose pro jakoukoli potenciální základní třídu, je nutné zadat následující:

  • DisposeImplementace, která volá Dispose(bool) metodu.
  • Dispose(bool)Metoda, která provede skutečné vyčištění.
  • Buď třída odvozená z SafeHandle , která zabalí váš nespravovaný prostředek (doporučeno), nebo přepíše Object.Finalize metodu. SafeHandleTřída poskytuje finalizační metodu, takže nemusíte psát sami sebe.

Důležité

Základní třída může odkazovat pouze na spravované objekty a implementovat vzor Dispose. V těchto případech není finalizační metoda nutná. Finalizační metoda je nutná pouze v případě, že přímo odkazujete na nespravované prostředky.

Zde je obecný vzor pro implementaci vzoru dispose pro základní třídu, která používá bezpečný popisovač.

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

class BaseClassWithSafeHandle : IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose() => Dispose(true);

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _disposed = true;
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class BaseClassWithSafeHandle : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
            ' Free any other managed objects here.
            '
        End If

        disposed = True
    End Sub
End Class

Poznámka

Předchozí příklad používá SafeFileHandle objekt k ilustraci vzoru; SafeHandle namísto toho lze použít libovolný objekt odvozený od. Všimněte si, že v příkladu není správně vytvořena instance SafeFileHandle objektu.

Zde je obecný vzor pro implementaci vzoru dispose pro základní třídu, která přepisuje Object.Finalize .

using System;

class BaseClassWithFinalizer : IDisposable
{
    // To detect redundant calls
    private bool _disposed = false;

    ~BaseClassWithFinalizer() => Dispose(false);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;
    }
}
Class BaseClassWithFinalizer : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
End Class

Tip

V jazyce C# vytvoříte finalizační metodu tím, že poskytnete destruktor, nikoli Object.Finalize . v Visual Basic vytvoříte finalizační metodu pomocí Protected Overrides Sub Finalize() .

Implementace vzoru dispose pro odvozenou třídu

Třída odvozená od třídy, která implementuje IDisposable rozhraní, by neměla implementovat IDisposable , protože implementace základní třídy IDisposable.Dispose je zděděna svými odvozenými třídami. Místo toho pro vyčištění odvozené třídy zadejte následující:

  • protected override void Dispose(bool)Metoda, která přepisuje metodu základní třídy a provede skutečné vyčištění odvozené třídy. tato metoda musí také volat base.Dispose(bool) metodu ( MyBase.Dispose(bool) v Visual Basic) základní třídy a předat její stav disposing argumentu.
  • Buď třída odvozená z SafeHandle , která zabalí váš nespravovaný prostředek (doporučeno), nebo přepíše Object.Finalize metodu. SafeHandleTřída poskytuje finalizační metodu, která vám uvolní, abyste ji nemuseli nakódovat. Pokud zadáte finalizační metodu, musí volat Dispose(bool) přetížení s disposing argumentem false .

Toto je obecný vzor implementace vzoru Dispose pro odvozenou třídu, která používá bezpečný popisovač:

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

class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
{
    // To detect redundant calls
    private bool _disposed = false;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
           // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        _disposed = true;

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class DerivedClassWithSafeHandle : Inherits BaseClassWithSafeHandle
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

        ' Call base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Poznámka

Předchozí příklad používá SafeFileHandle objekt k ilustraci vzoru; SafeHandle namísto toho lze použít libovolný objekt odvozený od. Všimněte si, že v příkladu není správně vytvořena instance SafeFileHandle objektu.

Zde je obecný vzor pro implementaci vzoru dispose pro odvozenou třídu, která přepisuje Object.Finalize :

class DerivedClassWithFinalizer : BaseClassWithFinalizer
{
    // To detect redundant calls
    bool _disposed = false;

    ~DerivedClassWithFinalizer() => this.Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        if (disposing)
        {
            // TODO: dispose managed state (managed objects).
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.
        _disposed = true;

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Class DerivedClassWithFinalizer : Inherits BaseClassWithFinalizer
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
End Class

Implementace vzoru dispose pomocí bezpečných popisovačů

Následující příklad znázorňuje vzor Dispose pro základní třídu, DisposableStreamResource , který používá bezpečný popisovač k zapouzdření nespravovaných prostředků. Definuje třídu, DisposableStreamResource která používá k SafeFileHandle zabalení Stream objektu, který představuje otevřený soubor. Třída obsahuje také jednu vlastnost Size , která vrací celkový počet bajtů v datovém proudu souboru.

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

public class DisposableStreamResource : IDisposable
{
    // Define constants.
    protected const uint GENERIC_READ = 0x80000000;
    protected const uint FILE_SHARE_READ = 0x00000001;
    protected const uint OPEN_EXISTING = 3;
    protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
    private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);

    // Define Windows APIs.
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
    protected static extern SafeFileHandle CreateFile(
        string lpFileName, uint dwDesiredAccess,
        uint dwShareMode, IntPtr lpSecurityAttributes,
        uint dwCreationDisposition, uint dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("kernel32.dll")]
    private static extern int GetFileSize(
        SafeFileHandle hFile, out int lpFileSizeHigh);

    // Define locals.
    private bool _disposed = false;
    private readonly SafeFileHandle? _safeHandle;
    private readonly int _upperWord;

    public DisposableStreamResource(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            throw new ArgumentException("The fileName cannot be null or an empty string");
        }

        _safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

        // Get file size.
        Size = GetFileSize(_safeHandle, out _upperWord);
        if (Size == INVALID_FILE_SIZE)
        {
            Size = -1;
        }
        else if (_upperWord > 0)
        {
            Size = (((long)_upperWord) << 32) + Size;
        }
    }

    public long Size { get; }

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

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        // Dispose of managed resources here.
        if (disposing)
        {
            _safeHandle?.Dispose();
        }

        // Dispose of any unmanaged resources not wrapped in safe handles.

        _disposed = true;
    }
}
Imports Microsoft.Win32.SafeHandles

Public Class DisposableStreamResource : Implements IDisposable
    ' Define constants.
    Protected Const GENERIC_READ As UInteger = &H80000000UI
    Protected Const FILE_SHARE_READ As UInteger = &H0I
    Protected Const OPEN_EXISTING As UInteger = 3
    Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
    Private Const INVALID_FILE_SIZE As Integer = &HFFFFFFFF

    ' Define Windows APIs.
    Protected Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (
        lpFileName As String, dwDesiredAccess As UInt32,
        dwShareMode As UInt32, lpSecurityAttributes As IntPtr,
        dwCreationDisposition As UInt32, dwFlagsAndAttributes As UInt32,
        hTemplateFile As IntPtr) As SafeFileHandle

    Private Declare Function GetFileSize Lib "kernel32" (
        hFile As SafeFileHandle, ByRef lpFileSizeHigh As Integer) As Integer

    ' Define locals.
    Private disposed As Boolean = False
    Private ReadOnly safeHandle As SafeFileHandle
    Private ReadOnly upperWord As Integer

    Public Sub New(fileName As String)
        If String.IsNullOrWhiteSpace(fileName) Then
            Throw New ArgumentNullException(
                NameOf(fileName), "The fileName cannot be null or an empty string")
        End If

        safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)

        ' Get file size.
        Size = GetFileSize(safeHandle, upperWord)
        If Size = INVALID_FILE_SIZE Then
            Size = -1
        ElseIf upperWord > 0 Then
            Size = (CLng(upperWord) << 32) + Size
        End If
    End Sub

    Public ReadOnly Property Size As Long

    Public Sub Dispose() _
              Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Exit Sub

        ' Dispose of managed resources here.
        If disposing Then
            safeHandle.Dispose()
        End If

        ' Dispose of any unmanaged resources not wrapped in safe handles.

        disposed = True
    End Sub
End Class

Implementace vzoru Dispose pro odvozenou třídu s bezpečnými popisovači

Následující příklad znázorňuje vzor Dispose pro odvozenou třídu , která dědí z třídy uvedené DisposableStreamResource2 DisposableStreamResource v předchozím příkladu. Třída přidá další metodu a používá objekt k zabalení WriteFileInfo SafeFileHandle popisovače zapisovatelného souboru.

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

public class DisposableStreamResource2 : DisposableStreamResource
{
    // Define additional constants.
    protected const uint GENERIC_WRITE = 0x40000000;
    protected const uint OPEN_ALWAYS = 4;

    // Define additional APIs.
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    protected static extern bool WriteFile(
        SafeFileHandle safeHandle, string lpBuffer,
        int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten,
        IntPtr lpOverlapped);

    // To detect redundant calls
    private bool _disposed = false;
    private bool _created = false;
    private SafeFileHandle _safeHandle;
    private readonly string _fileName;

    public DisposableStreamResource2(string fileName) : base(fileName) => _fileName = fileName;

    public void WriteFileInfo()
    {
        if (!_created)
        {
            _safeHandle = CreateFile(
                @".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            _created = true;
        }

        string output = $"{_fileName}: {Size:N0} bytes\n";
        _ = WriteFile(_safeHandle, output, output.Length, out _, IntPtr.Zero);
    }

    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        // Release any managed resources here.
        if (disposing)
        {
            // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

        // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
        // TODO: set large fields to null.

        _disposed = true;

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Imports Microsoft.Win32.SafeHandles

Public Class DisposableStreamResource2 : Inherits DisposableStreamResource
    ' Define additional constants.
    Protected Const GENERIC_WRITE As Integer = &H40000000
    Protected Const OPEN_ALWAYS As Integer = 4

    ' Define additional APIs.
    Protected Declare Function WriteFile Lib "kernel32.dll" (
        safeHandle As SafeFileHandle, lpBuffer As String,
        nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer,
        lpOverlapped As Object) As Boolean

    ' Define locals.
    Private disposed As Boolean = False
    Private created As Boolean = False
    Private safeHandle As SafeFileHandle
    Private ReadOnly filename As String

    Public Sub New(filename As String)
        MyBase.New(filename)
        Me.filename = filename
    End Sub

    Public Sub WriteFileInfo()
        If Not created Then
            safeHandle = CreateFile(
                ".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
            created = True
        End If

        Dim output As String = $"{filename }: {Size:N0} bytes {vbCrLf }"
        Dim result = WriteFile(safeHandle, output, output.Length, 0&, Nothing)
    End Sub

    Protected Overridable Overloads Sub Dispose(disposing As Boolean)
        If disposed Then Exit Sub

        ' Release any managed resources here.
        If disposing Then
            safeHandle?.Dispose()
        End If

        disposed = True
        ' Release any unmanaged resources not wrapped by safe handles here.

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

Viz také