Udostępnij za pośrednictwem


Okres istnienia obiektu: jak obiekty są tworzone i niszczone (Visual Basic)

Wystąpienie klasy , obiektu, jest tworzone przy użyciu słowa kluczowego New . Zadania inicjowania często muszą być wykonywane na nowych obiektach przed ich zastosowaniem. Typowe zadania inicjowania obejmują otwieranie plików, łączenie się z bazami danych i odczytywanie wartości kluczy rejestru. Visual Basic steruje inicjowaniem nowych obiektów przy użyciu procedur nazywanych konstruktorami (specjalne metody, które umożliwiają kontrolę nad inicjowaniem).

Po opuszczeniu zakresu obiekt jest zwalniany przez środowisko uruchomieniowe języka wspólnego (CLR). Visual Basic steruje wydawaniem zasobów systemowych przy użyciu procedur nazywanych destruktorami. Razem konstruktory i destruktory wspierają tworzenie niezawodnych i przewidywalnych bibliotek klas.

Używanie konstruktorów i destruktorów

Konstruktory i destruktory kontrolują tworzenie i niszczenie obiektów. Procedury Sub New i Sub Finalize w Visual Basic inicjalizują i niszczą obiekty; zastępują metody Class_Initialize i Class_Terminate używane w Visual Basic 6.0 i starszych wersjach.

Sub New

Konstruktor Sub New może działać tylko raz po utworzeniu klasy. Nie można go wywołać jawnie w dowolnym miejscu innym niż w pierwszym wierszu kodu innego konstruktora z tej samej klasy lub z klasy pochodnej. Ponadto kod w metodzie Sub New zawsze jest uruchamiany przed jakimkolwiek innym kodem w klasie. Program Visual Basic niejawnie tworzy Sub New konstruktor w czasie wykonywania, jeśli nie zdefiniujesz jawnie procedury Sub New dla klasy.

Aby utworzyć konstruktor dla klasy, utwórz procedurę o nazwie Sub New w dowolnym miejscu w definicji klasy. Aby utworzyć konstruktor sparametryzowany, określ nazwy i typy danych argumentów tak Sub New samo jak argumenty dla każdej innej procedury, jak w poniższym kodzie:

Sub New(ByVal s As String)

Konstruktory są często przeciążone, jak w poniższym kodzie:

Sub New(ByVal s As String, i As Integer)

Podczas definiowania klasy pochodnej z innej klasy pierwszy wiersz konstruktora musi być wywołaniem konstruktora klasy bazowej, chyba że klasa bazowa ma dostępny konstruktor, który nie przyjmuje żadnych parametrów. Wywołanie do klasy bazowej, która na przykład zawiera powyższy konstruktor, wyglądałoby tak: MyBase.New(s). W przeciwnym razie MyBase.New jest opcjonalny, a środowisko uruchomieniowe języka Visual Basic wywołuje go niejawnie.

Po zapisaniu kodu w celu wywołania konstruktora obiektu nadrzędnego można dodać do procedury dowolny dodatkowy kod inicjowania Sub New . Sub New może akceptować argumenty, gdy są wywoływane jako konstruktor sparametryzowany. Te parametry są przekazywane z procedury wywołującej konstruktor, na przykład Dim AnObject As New ThisClass(X).

Finalizowanie podrzędne

Przed zwolnieniem obiektów clR automatycznie wywołuje metodę Finalize dla obiektów, które definiują procedurę Sub Finalize . Metoda Finalize może zawierać kod, który musi zostać wykonany tuż przed zniszczeniem obiektu, na przykład kod do zamykania plików i zapisywania informacji o stanie. Istnieje niewielka utrata wydajności podczas wykonywania Sub Finalize, dlatego należy zdefiniować metodę Sub Finalize tylko wtedy, gdy jest potrzeba jawnego zwolnienia obiektów.

Uwaga / Notatka

Zbieracz śmieci w CLR nie usuwa (i nie może usuwać) niezarządzanych obiektów, które są wykonywane bezpośrednio przez system operacyjny, poza środowiskiem CLR. Dzieje się tak, ponieważ różne niezarządzane obiekty muszą być usuwane na różne sposoby. Te informacje nie są bezpośrednio skojarzone z niezarządzanym obiektem; należy je znaleźć w dokumentacji obiektu. Klasa, która używa niezarządzanych obiektów, musi usunąć je w swojej Finalize metodzie.

Destruktor Finalize to chroniona metoda, która może być wywoływana tylko z klasy, do której należy, lub z klas pochodnych. System wywołuje Finalize automatycznie, gdy obiekt zostanie zniszczony, dlatego nie należy jawnie wywoływać Finalize z zewnątrz implementacji klasy pochodnej Finalize .

W przeciwieństwie do metody Class_Terminate, która wykonywana jest natychmiast po ustawieniu obiektu na wartość nic, zwykle występuje opóźnienie między utratą zakresu a wywołaniem destruktora Finalize w języku Visual Basic. Program Visual Basic .NET umożliwia drugi rodzaj destruktora, IDisposable.Disposektóry można jawnie wywołać w dowolnym momencie, aby natychmiast zwolnić zasoby.

Uwaga / Notatka

Destruktor Finalize nie powinien zgłaszać wyjątków, ponieważ nie można ich obsłużyć przez aplikację i może spowodować zakończenie działania aplikacji.

Jak działają metody 'new' i 'finalize' w hierarchii klas

Za każdym razem, gdy wystąpienie klasy jest tworzone, środowisko uruchomieniowe języka wspólnego (CLR) próbuje wykonać procedurę o nazwie New, jeśli istnieje w tym obiekcie. New jest typem procedury nazywanej constructor , która służy do inicjowania nowych obiektów przed wykonaniem jakiegokolwiek innego kodu w obiekcie. Konstruktor New może służyć do otwierania plików, nawiązywania połączenia z bazami danych, inicjowania zmiennych i dbania o wszelkie inne zadania, które należy wykonać, zanim będzie można użyć obiektu.

Po utworzeniu Sub New wystąpienia klasy pochodnej, najpierw wykonywany jest konstruktor klasy bazowej, a następnie wykonywane są konstruktory w klasach pochodnych. Dzieje się tak, ponieważ pierwszy wiersz kodu w konstruktorze Sub New używa składni MyBase.New()do wywoływania konstruktora klasy bezpośrednio powyżej samej w hierarchii klas. Sub New Konstruktor jest następnie wywoływany dla każdej klasy w hierarchii klas, aż do osiągnięcia konstruktora dla klasy bazowej. W tym momencie uruchamiany jest kod w konstruktorze klasy bazowej, po czym wykonywany jest kod w konstruktorach wszystkich klas pochodnych, a na końcu wykonywany jest kod w konstruktorach najbardziej wyspecjalizowanych klas.

Zrzut ekranu przedstawiający konstruktory hierarchii klas i dziedziczenie.

Gdy obiekt nie jest już potrzebny, clR wywołuje metodę Finalize dla tego obiektu przed zwolnieniem pamięci. Metoda Finalize jest nazywana destructor, ponieważ wykonuje czynności porządkowe, takie jak zapisywanie informacji o stanie, zamykanie plików i połączeń z bazami danych oraz inne zadania, które należy wykonać przed zwolnieniem obiektu.

Zrzut ekranu przedstawiający metodę Finalize oraz jej destruktor.

Interfejs IDisposable

Wystąpienia klas często kontrolują zasoby, które nie są zarządzane przez clR, takie jak obsługa systemu Windows i połączenia bazy danych. Te zasoby muszą zostać usunięte w Finalize metodzie klasy, tak aby były zwalniane, gdy obiekt zostanie zniszczony przez moduł odśmiecenia pamięci. Jednak moduł odśmieceń pamięci niszczy obiekty tylko wtedy, gdy clR wymaga więcej wolnej pamięci. Oznacza to, że zasoby mogą nie być zwalniane jeszcze przez długi czas po tym, jak obiekt przekroczy zakres.

Aby uzupełnić zbiór odpadów, klasy mogą zapewniać mechanizm aktywnego zarządzania zasobami systemowymi, jeśli implementują interfejs IDisposable. IDisposable ma jedną metodę , Disposektórą klienci powinni wywołać po zakończeniu korzystania z obiektu. Możesz użyć Dispose metody , aby natychmiast zwolnić zasoby i wykonywać zadania, takie jak zamykanie plików i połączeń bazy danych. W przeciwieństwie do Finalize destruktora, metoda Dispose nie jest wywoływana automatycznie. Klienci klasy muszą jawnie wywoływać Dispose , gdy chcesz natychmiast zwolnić zasoby.

Implementowanie interfejsu IDisposable

Klasa, która implementuje IDisposable interfejs, powinna zawierać następujące sekcje kodu:

  • Pole do śledzenia, czy obiekt został usunięty:

    Protected disposed As Boolean = False
    
  • Przeciążenie Dispose zwalniające zasoby klasy. Ta metoda powinna być wywoływana przez metody Dispose i Finalize klasy bazowej.

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposed Then
            If disposing Then
                ' Insert code to free managed resources.
            End If
            ' Insert code to free unmanaged resources.
        End If
        Me.disposed = True
    End Sub
    
  • Implementacja tej funkcji Dispose zawiera tylko następujący kod:

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
  • Zastąpienie Finalize metody zawierającej tylko następujący kod:

    Protected Overrides Sub Finalize()
        Dispose(False)
        MyBase.Finalize()
    End Sub
    

Wyprowadzanie z klasy implementującej IDisposable

Klasa, która pochodzi z klasy bazowej, która implementuje IDisposable interfejs, nie musi zastępować żadnej z metod podstawowych, chyba że używa dodatkowych zasobów, które należy usunąć. W takiej sytuacji klasa pochodna powinna zastąpić metodę klasy bazowej, aby usunąć zasoby klasy Dispose(disposing) pochodnej. To zastąpienie musi wywołać metodę klasy bazowej Dispose(disposing) .

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    If Not Me.disposed Then
        If disposing Then
            ' Insert code to free managed resources.
        End If
        ' Insert code to free unmanaged resources.
    End If
    MyBase.Dispose(disposing)
End Sub

Klasa pochodna nie powinna zastępować metod Dispose i Finalize klasy bazowej. Gdy metody te są wywoływane z wystąpienia klasy pochodnej, implementacja tych metod w klasie bazowej wywołuje przesłoniętą metodę Dispose(disposing) klasy pochodnej.

Zbieranie śmieci i finalizowanie destruktora

Program .NET Framework używa systemu zbierania śmieci śledzącego odwołania do okresowego zwalniania nieużywanych zasobów. W programie Visual Basic 6.0 i starszych wersjach użyto innego systemu o nazwie zliczanie odwołań do zarządzania zasobami . Chociaż oba systemy wykonują tę samą funkcję automatycznie, istnieje kilka ważnych różnic.

ClR okresowo niszczy obiekty, gdy system określa, że takie obiekty nie są już potrzebne. Obiekty są zwalniane szybciej, gdy zasoby systemowe są na wyczerpaniu, a rzadziej w innym przypadku. Opóźnienie między utratą zakresu przez obiekt a jego zwolnieniem przez CLR oznacza, że w przeciwieństwie do obiektów w języku Visual Basic 6.0 i wcześniejszych wersjach, nie można dokładnie określić, kiedy obiekt zostanie zniszczony. W takiej sytuacji mówi się, że obiekty mają niedeterministyczny okres istnienia. W większości przypadków niedeterministyczny okres istnienia nie zmienia sposobu pisania aplikacji, o ile pamiętasz, że Finalize destruktor może nie być wykonywany natychmiast, gdy obiekt opuści zakres.

Kolejna różnica między systemami odzyskiwania pamięci obejmuje użycie elementu Nothing. Aby skorzystać z zliczania odwołań w Visual Basic 6.0 i starszych wersjach, programiści czasami przypisywali Nothing do zmiennych obiektów w celu zwolnienia odwołań, które przechowywały te zmienne. Jeśli zmienna przechowywała ostatnie odwołanie do obiektu, zasoby obiektu zostały natychmiast zwolnione. W późniejszych wersjach języka Visual Basic, mimo że mogą występować przypadki, w których ta procedura jest nadal cenna, jej wykonanie nigdy nie powoduje natychmiastowego zwolnienia zasobów przez obiekt, na który się powołano. Aby natychmiast zwolnić zasoby, użyj metody obiektu Dispose , jeśli jest dostępna. Jedyną sytuacją, w której należy ustawić zmienną na Nothing, jest ta, gdy jej okres istnienia jest długi w stosunku do czasu, którego potrzeba, aby moduł zarządzania pamięcią wykrył osierocone obiekty.

Zobacz także