Zamykanie pliku w chwili wywołania destruktora klasy?

0

Witam. Pisząc od nowa serwer komunikatora, na starcie spotkałem się z czymś takim:

  • wywołuję Thread.CurrentThread.Abort(), co powoduje zamknięcie aplikacji (konsola)
  • wywołuje się destruktor mojego obiektu
  • zamykam połączenie z bazą i właśnie tutaj zapisuję do log'a to co trzeba
  • no i młotek z gwoździami, Cannot access a closed file. :(

Po tej instrukcji właśnie miał zostać zamknięty log przeze mnie. Niestety program nie zdąży nawet do tego dojść.

Czy jest tak, że w chwili wychodzenia z programu, a tu niszczenia mojego obiektu, wszystkie uchwyty zew. zostają zwolnione? Czy może coś nie tak naklepałem? Pomóżcie.

Dodam, że strumień pliku leży sobie w statycznej klasie i jest niezależny od głównej klasy, tzn. najpierw jest inicjalizacja log'a, potem zapisywanie i zamknięcie z klasy głównej, ale do tego zamknięcia nie dochodzi, bo poprzedza go zapis ostatniej linijki do log'a i występuje wyjątek :/ Kod:

class XMServer
{
    public XMServer()
    {
        Log.Initialize(); // otwarcie pliku
        
        // ...

        // zamknięcie w razie błędów
    }

    ~XMServer()
    {
        db.Close(); // w tej metodzie jest odpowiedni zapis do log'a, w jej wnętrzu dostaję wyjątek przy metodzie StreamWriter.Write(...);
        Log.Close(); // zamknięcie log'a
    }
}
0

Wydaje mi się że w ten sposób mógłbyś sprawę rozwiązać w C++, ale w C# destruktory działają inaczej (pomijając że są wywoływane automatycznie, a nie ręcznie jak w C++). W C# takie rzeczy najlepiej robić implementując odpowiednio interjes IDisposable, tu masz to dobrze wyjaśnione: http://www.pzielinski.com/?p=111
Jeśli gadam głupoty to prosze mnie opieprzyć i poprawić ;P

0

No tak, w którejś lekturze uczyli przecież, że zasoby zwalnia się w metodzie Dispose(). Na co dzień nie korzystam z tego i jakoś wyleciało z głowy. Dzięki, jeżeli to naprawi problem zaakceptuję odp.

Tylko teraz analizując jeszcze raz, co mi to da? I tak w destruktorze wywołuję Dispose() czyli wtedy już nie mam dostępu do pliku. :/

0
xeo545x39 napisał(a)

Witam. Pisząc od nowa serwer komunikatora, na starcie spotkałem się z czymś takim:

  • wywołuję Thread.CurrentThread.Abort(), co powoduje zamknięcie aplikacji (konsola)

No i to jest Twój pierwszy błąd i zapewne źródło problemów. Czemu to robisz? Czy jeśli chcesz zatrzymać rower, to wkładasz rękę w szprychy?

0

A jak prawidłowo zatrzymać działanie programu z klasy? (oczywiście mowa o konsoli)

0

Jak to z klasy? Program kończy się wtedy, gdy już wszystko zrobi, albo gdy użytkownik zechce go wyłączyć.

0

No właśnie, tuż po utworzeniu obiektu XMServer sterowanie przechodzi do tego właśnie obiektu. Najpierw konstruktor, który robi tam sobie różne rzeczy, a potem znów z Main'a robię Run'a serwera i tam już siedzi na stałe sterowanie. Tyle, że przy błędzie połączenia z bazą danych aplikacja powinna dać log'a, czekać na naciśnięcie klawisza (zapobiegawczo, w celu przeczytania komunikatu w konsoli) i po klawiszu wyłączyć się. To jak mam inaczej wyłączyć ze środka programu, dokładniej:

public XMServer()
        {
            Log.Initialize();

            try
            {
                // ...

                // connect to the database
                db = new DatabaseControl();
                db.Open(); // łączę się z bazą, jak coś nie tak, wywali wyjątek ten poniżej
            }
            catch (MySqlException e)
            {
                Log.Write("MySQL: " + e.Message + " (" + e.Number + ")");
                Console.ReadKey();
                Thread.CurrentThread.Abort(); // tu powinien zamknąć się serwer
            }

I jak to zrobić bez abortowania wątku ani zabijaniu procesu?

0

Ty w konstruktorze zabijasz wątek?

0

Dobreee.

Wątek skończy się sam, gdy już nie będzie miał nic do roboty. Absolutnie nie powinieneś w ten sposób ubijać własnego wątku (żadnego innego również). Rzuć wyjątek i tyle.

0

Kurde, bo jakoś totalnie mnie zatkało :| Tworzę serwer i w konstruktorze łączę się z bazą - jeżeli wystąpi błąd, to co teraz? Wywalam wyjątek, no ok, zapisuję log'a o wyjątku i teraz powinien zakończyć swoje działanie program bez użycia wspomagaczy typu abortowanie wątku. Tyle, że czeka na klawisz. Po złapaniu klawisza co powinno się zrobić żeby dać kontrolę do Maina, a tam się zakończy program, bo już nic do roboty nie będzie? Wiem jak to zrobić, ale jest jeden mankament, że za blokiem try są jeszcze instrukcje, które powinny się wywołać jak jest wszystko ok. To jak dać te instrukcje do try? Wtedy po wyjątku wyjdzie z konstruktora do Maina i tam zakończy się działanie. Dobrze?

0

Nie łap tego wyjątku w konstruktorze tylko wyżej. Np. w main.

0

Dobra, poprawiłem nie ma już dziwnych rzeczy w kodzie, ale dalej to samo, ehh.. Program teraz normalnie kończy pracę, dochodzi do końca Maina, wywołuje się destruktor i już mam zamknięty plik.

1

Po pierwsze to przenieś połączenie z bazą z konstruktora do jakiejś metody. Rzucanie wyjątku w trakcie tworzenia konstruktorze obiektu nie jest za bardzo fajną metodą, bo nie zostanie wywołana metoda Dispose w trakcie zwalniania obiektu z pamięci.

0

Uporałem się z tym jakoś, dałem zmienne pomocnicze bool Running i bool IsReady, odpowiednie operacje się wykonują, jeżeli będzie coś nie tak, rzuca wyjątek i zmienne nie zostaną ustawione na true, więc aplikacja dalej nie przejdzie, tylko się wyłączy z maina. Na końcu maina daję stop serwera i tam zwalniam zasoby, niestety w przypadku zamknięcia krzyżykiem konsoli nic się nie stanie, bo teraz nie ma destruktora, bo po co, jak i tak nie wykonuje do końca co chcę.

1
xeo545x39 napisał(a)

Kurde, bo jakoś totalnie mnie zatkało :| Tworzę serwer i w konstruktorze łączę się z bazą - jeżeli wystąpi błąd, to co teraz?

To jest właśnie błąd.
Konstruktor ma stworzyć obiekt, coś tak banalnego jak tworzenie obiektu musi się zawsze udać. Pomyśl - przecież gdyby int x = 7; rzucało wyjątek, to byłby jakiś absurd, a Ty coś takiego chcesz zrobić.
W konstruktorze nigdy nie odczytujemy plików, nie łączymy się z bazą danych, nie łączymy się po sieci, to jest miejsce wyłącznie na inicjalizację pól klasy, a nie na zaszywanie tam logiki aplikacji, zwłaszcza takiej, która może rzucić wyjątek.

0

Dzięki somekind za radę, tak na prawdę to nie wiedziałem, że nie powinno się takich rzeczy robić w konstruktorze. Jeszcze raz przeprojektuje kod na właściwy czyli łączenie z bazą itd. w metodzie i zależnie czy rzuci wyjątkiem czy nie, włączy się serwer albo się wyłączy.

0

Watek, o ile moze byc anulowany (bo abort to nie jest w zadnym wypadku pauza) to z wewnatrz, za pomoca zmiany flagi (Backgroundworker jest zaimplementowany w taki sposob, mozesz przejzec jego implementacje, gdy zrozumiesz koncept MS bedzie Ci latwiej tworzyc wlasne watki).

Co do zwalniania zasobow, to temat jest niezmiernie wazny, szczegolnie podczas uzywania interopow, tutaj sa dwa fajnie napisane artykuly:
http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx Implementing Finalize and Dispose to Clean Up Unmanaged Resources
http://msdn.microsoft.com/en-us/library/ms973872.aspx An Overview of Managed/Unmanaged Code Interoperability

Polecam doinstalowanie MSDN do VisualStudio i korzystanie z opisow metod podczas implementowania, szczegolnie w poczatkowych przygodach z programowaniem, lub podczas uzywania wczesniej nieznanych przestrzeni. Czesto pozwala to znalezc luki w kodzie jeszcze podczas pisania :)
Dla przykladu definicja Thread.Abort
http://msdn.microsoft.com/en-us/library/ty8d3wta.aspx

1 użytkowników online, w tym zalogowanych: 0, gości: 1