moukoublen Δημοσ. 13 Οκτωβρίου 2012 Μέλος Δημοσ. 13 Οκτωβρίου 2012 Οχι βεβαια!!!! Φαντασου να εχεις ενα resource στην main και αυτο να το περνας σε μια φορμα (απλο reference). Τοτε οταν κλεισεις την φορμα το resource θα απελευθερωθει, κατι που σιγουρα δεν θελεις εφοσον ειναι reference Απόλυτα λογικό. Άρα θα πρέπει να καλείς το Dispose του κάθε DataTable που μπορεί να δημιουργήσεις μέσα σε μία φόρμα αλλιώς θα έχεις leak; Θα πρέπει να καλείς και Dispose για κάθε DataColumn αυτού του DataTable (μιας και τα DataColumn είναι disposable); Εκεί είναι λίγο που αρχίζει και μπερδεύει το πράγμα. Γιατί για παράδειγμα στον ίδιο τον κώδικα του DataTable στο Dispose, δεν καλείται κανένα despose κανενός Column. > // MarshalByValueComponent.cs που κληρονομεί το DataTable.cs protected virtual void Dispose(bool disposing) { if (disposing) { lock(this) { if (site != null && site.Container != null) { site.Container.Remove(this); } if (events != null) { EventHandler handler = (EventHandler)events[EventDisposed]; if (handler != null) handler(this, EventArgs.Empty); } } } } Στέκομαι σε αυτό γιατί ενώ για τα πάντα είμαι πολύ προσεκτικός (using ή dispose αν δεν είναι μέσα σε using. Άσχετα με το αν βρίσκονται με τη σειρά τους μέσα σε Disposable αντικείμενο ή όχι.) για το DataTable δεν το έκανα μέχρι στιγμής. Ήξερα ότι είναι Disposable αλλά είχα αρκεστεί στην φράση αυτή του βιβλίου και γιατί το θέμα με το DataColumn μου φαίνεται υπερβολικό.
παπι Δημοσ. 13 Οκτωβρίου 2012 Δημοσ. 13 Οκτωβρίου 2012 Εγω παιζω μονο με ef. Βαζω το entity σε ενα using και καθαρισα. Ουτε leeks ουτε τιποτα. Το πως δουλευεις με datatalbe.. δεν εχω ιδεα, δεν εχω ασχοληθει.
moukoublen Δημοσ. 13 Οκτωβρίου 2012 Μέλος Δημοσ. 13 Οκτωβρίου 2012 Μα η πλάκα είναι ότι δεν έχω καθόλου εμφανή leaks μέχρι στιγμής και η εφαρμογή παίζει 1 χρόνο τώρα. Τέλος πάντων ευχαριστώ για τις απαντήσεις, αν κανείς ξέρει περισσότερα θα του είμαι υπόχρεος.
albNik Δημοσ. 13 Οκτωβρίου 2012 Δημοσ. 13 Οκτωβρίου 2012 Τα DataTable και DataColumn είναι τελείως managed. Δεν χρειάζονται το Dispose. Το κληρονόμησαν από την MarshalByValueComponent αλλά δεν το χρειάζονται.
moukoublen Δημοσ. 13 Οκτωβρίου 2012 Μέλος Δημοσ. 13 Οκτωβρίου 2012 Πάντως και το ίδιο το desingner το Dispose που παράγει για τη form κάνει dispose μόνο τον IContainer (που δε ξέρω που το κάνει initialize) και καλεί και της base. Δεν κάνει Dispose ένα ένα τα components που δημιουργούνται εσωτερικά της. Τέλος πάντων πρέπει να φύγω τώρα και ίσως μου διαφεύγουν πράγματα. Τα DataTable και DataColumn είναι τελείως managed. Δεν χρειάζονται το Dispose. Το κληρονόμησαν από την αλλά δεν το χρειάζονται. Μάλλον (μακάρι) έτσι πρέπει να έιναι. (Αλλιώς πρέπει να προσθέσω καμιά 30αρια Dispose - εντάξει δεν είναι και τίποτα)
defacer Δημοσ. 14 Οκτωβρίου 2012 Δημοσ. 14 Οκτωβρίου 2012 Πολύ δημοφιλές θέμα γενικότερα. Επειδή απαντώντας σε μεμονωμένα quotes δε μου έβγαινε καλά, (ελπίζω πως) αναλύω όλα τα σχετικά θέματα με το παρακάτω κατεβατό. Σε .ΝΕΤ έχουμε garbage collector για να κανονίζει την απελευθέρωση της μνήμης, όμως δεν υπάρχει κάποιος άλλος αυτόματος μηχανισμός που να κανονίζει την απελευθέρωση των λεγόμενων unmanaged resources, όπως είναι π.χ. τα διάφορα OS handles. Επομένως όσον αφορά αυτό το τμήμα της διαχείρισης πόρων, you are back to square one και υπάρχουν τα ίδια "προβλήματα" που υπάρχουν σε unmanaged περιβάλλοντα. Θα κάνω τώρα έναν παραλληλισμό με τη C++. Στη C++ λοιπόν όταν θέλεις να απελευθερώσεις resources το κάνεις μέσα στον destructor. Βεβαίως αν έχεις κάποιο αντικείμενο του οποίου ο destructor δεν θα κληθεί ποτέ (π.χ. λόγω memory leak) τότε θα έχεις και το αντίστοιχο resource leak επειδή ο destructor δε θα τρέξει ποτέ. Σε C# ο destructor έχει την ίδια σύνταξη με τη C++ αλλά ονομάζεται finalizer: >class Foo { ~Foo() { /* finalizer */ } } Επίσης διαφέρει στη συμπεριφορά του κατά τους εξής τρόπους: Ο finalizer **δεν** καλείται αυτόματα παρά μόνο σε μία περίπτωση Δηλαδή όταν το αντικείμενο είναι έτοιμο για garbage collection (δεν υπάρχουν άλλα references σ' αυτό) και υπάρχει finalizer, τότε το CLR αντί να κάνει collect κατευθείαν το βάζει σε μια λίστα και (αν θυμάμαι καλά) το κάνει finalize και collect μαζί σε δεύτερο χρόνο μαζί με άλλα παρόμοια αντικείμενα. Αυτό συμβαίνει διότι ο finalizer τρέχει κώδικα και άρα δεν ξέρουμε με βεβαιότητα πόση ώρα θα μας πάρει το finalization, άρα δε μπορούμε να το κάνουμε επιτόπου την ώρα που αποφασίσαμε ένα collectάκι στα γρήγορα. Οι τρόποι με τους οποίους μπορούμε να επηρρεάσουμε αυτή τη συμπεριφορά είναι δύο: 1. Αν δεν υπάρχει finalizer το CLR δε μπαίνει σ' αυτή τη διαδικασία. 2. Ένα αντικείμενο μπορεί να μαρκαριστεί ως "μην τρέξεις τον finalizer όταν κάνεις collect" καλώντας την GC.SuppressFinalize() όσο ακόμα έχουμε reference προς αυτό. Γιατί να θέλουμε να το κάνουμε αυτό; Γιατί ο finalizer καθυστερεί το collection όπως λέω παραπάνω, οπότε αν ξέρουμε πως δε χρειάζεται καλό είναι να μην τρέξει. Από την άλλη αν υπάρχει finalizer σημαίνει ότι χρειάζεται, άρα; Θα δούμε παρακάτω. Δεν μπορείς να ελέγξεις το πότε θα τρέξει ο finalizer Το συμπέρασμα αυτό βγαίνει ευθέως από τα παραπάνω. Αυτό όμως δημιουργεί ένα πρόβλημα: μπορεί ο finalizer να μην τρέξει επι πολλή ώρα αν δεν γίνουν αρκετά rounds από garbage collection κατά τη διάρκεια ζωής του process (σημειώνω ότι υπάρχουν "επίπεδα" garbage collection, δεν γίνονται collected όλα τα αντικείμενα μαζί), πράγμα που σημαίνει ότι εν δυνάμει έχουμε "προσωρινά" unmanaged resource leaks, κάτι που γενικά είναι προβληματικό. Ο finalizer απελευθερώνει μόνο unmanaged resources Και αυτό το συμπέρασμα βγαίνει από τα παραπάνω: εφόσον ο finalizer τρέχει μόνο αφότου το αντικείμενο έχει ήδη γίνει collected, αυτό σημαίνει πως τα όποια managed resources κατείχε (π.χ. references σε άλλα αντικείμενα) είναι κι αυτά πλέον ώριμα για collection ή έχουν ήδη γίνει κι αυτά collected. Επομένως όχι απλά δε χρειάζεται αλλά και δεν πρέπει να τα ακουμπήσει ο finalizer για κανένα λόγο. Εφόσον δε μπορούμε να ελέγξουμε το πότε τρέχει ο finalizer τότε τι γίνεται; Εδώ λοιπόν έρχεται το λεγόμενο dispose/finalize pattern, στο οποίο εμπλέκεται και το IDisposable. Οπότε ας κάνουμε μια παρένθεση για να δούμε τι γίνεται με το IDisposable και την Dispose(). Το MSDN λέει για το IDisposable: The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams. Το πρόβλημα εδώ είναι ότι δεν μπορούμε να είμαστε σίγουροι πως η Dispose() θα κληθεί σωστά, επομένως όταν θέλουμε να διαχειριστούμε unmanaged resources θα πρέπει είτε να πάρουμε το ρίσκο πως θα υπάρξει leak είτε να κάνουμε implement επιπλέον και ένα finalizer για σιγουριά. Επίσης, παρόλο που δεν αναφέρεται ρητά, και παρόλο που αυτός δεν είναι σωστός λόγος να κάνεις implement το IDisposable, στην Dispose() μπορείς επίσης να "βοηθήσεις" τον garbage collector "χάνοντας" τα references σε αντικείμενα που κρατάει η class σου και άρα κάνοντάς τα υποψήφια για collection άμεσα (διαφορετικά δεν μπορεί να γίνουν collected παρά μόνο τουλάχιστον ένα γύρο μετά από αυτόν στον οποίο θα γίνει collected το αντικείμενο που κρατάει τα references). Τέλος, αν κάποια αντικείμενα που κρατάς είναι επίσης IDisposable τότε μέσα στην Dispose() θα πρέπει να γίνουν και αυτά Dispose -- αυτό όμως δεν γίνεται αυτόματα, είναι απλά μια σύμβαση! Πάμε τώρα να δούμε πώς εφαρμόζονται όλα αυτά στο dispose/finalize pattern. Η ιδέα είναι πως με αυτό το pattern πετυχαίνουμε τα εξής: Δίνουμε τη δυνατότητα στο χρήστη της class να απελευθερώσει τα unmanaged resources που κρατάμε ντετερμινιστικά όποτε αυτός το επιθυμεί, καλώντας την Dispose(). Αν για οποιοδήποτε λόγο αυτό δε γίνει, υπάρχει finalizer στον οποίο θα απελευθερωθούν τα unmanaged resources όταν αυτός τρέξει. Αν η Dispose() κληθεί, αποφεύγουμε το extra overhead της αυτόματης κλήσης του finalizer αργότερα καλώντας επιτόπου την GC.SuppressFinalize(). Όλα αυτά φαίνονται σε δράση στην παρακάτω υποθετική class. >sealed class Foo : IDisposable { private IntPtr handle; // ένα unmanaged resource private Bar bar; // είναι κι αυτό IDisposable private string str; // αυτό δεν είναι private bool isDisposed; // απαραίτητο εφόσον είμαστε IDisposable public void Dispose() { this.Dispose(true); // απελευθέρωσε managed και unmanaged resources GC.SuppressFinalize(this); // να μη τρέξει ο finalizer, δε θα προσφέρει πλέον κάτι } ~Foo() { // απελευθέρωσε μόνο unmanaged resources -- τα managed δεν τα ακουμπάμε καν τώρα! this.Dispose(false); } private void Dispose(bool isDisposing) // αν δεν ήμασταν sealed θα ήταν protected virtual { if (this.isDisposed) { return; // προστασία από πολλαπλά dispose } if (isDisposing) { // απελευθέρωση managed resources -- γίνεται μόνο όταν κληθεί η Dispose() bar.Dispose(); // προφανές str = null; // ντεμέκ βοηθάμε το CLR να κάνει collect το str γρηγορότερα } // απελευθέρωση unmanaged resources -- γίνεται είτε επειδή Dispose() είτε επειδή finalizer SomehowRelease(handle); // και για να θυμόμαστε this.isDisposed = true; } } Και τώρα οι απαντήσεις σε quotes... Και η έξοδος που έχω είναι T2 Disposed Και τίποτε άλλο. Τι γίνεται;... Και γενικότερα (από κει που μου ξεκίνησε η απορία). Έστω ότι έχω μια φόρμα windows forms. Και μέσω ενός κουμπιού ανοίγει με ShowDialog μία άλλη φόρμα που έχει 1 DataTable εσωτερικά της (το οποίο είναι disposable) (δε μιλώ για τα gui components). Όταν θα κλείσει ο χρήστης τη φόρμα αυτή, το table θα γίνει dispose ή όχι; Και εάν όχι, κάθε φορά που θα ανοίγω το dialog αυτό, θα κρέμονται στη μνήμη DataTables; Δεν καλείς την T1.Dispose() και γι' αυτό δε βλέπεις κάτι άλλο (δε θα κληθεί ποτέ). Κάθε φορά που ανοίγεις το dialog θα κρέμονται DataTables μέχρι να γίνουν collected, και αν αυτά έχουν και unmanaged resources τότε μέχρι να κληθεί ο finalizer θα έχεις και προσωρινό resource leak. Θα γίνει dispose κανονικά μόλις το συλλέξει ο garbage collector. Αυτό είναι το νόημα του IDisposable. Όταν χειρίζεσαι unsafe code το κάνεις IDisposable για να απελευθερώνεις όση μνήμη δέσμευσες. Αν δεν καλέσεις μόνος σου την Dispose() την καλεί αυτόματα ο Garbage Collector. Αυτό είναι λάθος, δεν ισχύει. Ποτέ δεν καλεί τη Dispose() o Garbage Collector. Αυτοί είναι κανόνες που σέβεται το .ΝΕΤ και συνιστώνται γενικά. Απλή συνάρτηση σαν όλες της άλλες είναι. Σωστά. Άρα θα πρέπει κάθε disposable αντικείμενο το οποίο δεν είναι μέσα σε ενα using να καλείτε με το χέρι το dispose του κάποια στιγμή οπωσδήποτε. Άρα ακόμα και αν υπάρχουν 2 - 3 DataTable (που μπορεί να χρησιμοποιούνται σαν datasource για κάποια DataGridView) θα πρέπει έστω στο on_closed event να τα κάνει κανείς dispose διαφορετικά "κρέμονται" στη μνήμη; Ναι, με μια μικρή προσοχή όσον αφορά το τι σημαίνει "κρέμονται". Βεβαίως και έχει > class MyClass { ~MyClass() { Console.WriteLine("destructor"); } } static class Program { static void Main() { MyClass c = new MyClass(); } } Δεν είναι destructor, δε συμπεριφέρεται σα destructor και έχει σημαντικές διαφορές -- είναι "κατι σαν" destructor αλλά μέχρι εκεί. Μα η απορία μου είναι στο αν ισχύει το quote που λέει ότι καλείται αυτόματα αν στο dispose ενός disposable αντικειμένου που περιέχει ένα άλλο. Άρα αν έχεις μια class που περιέχει κάποια DIsposable objects θα πρέπει στον finalizer (destructor) να καλέσεις και το dispose αυτών των disposabe objects ώστε οτα το μαζέψει ο garbage collector να κληθεί και το dispose αυτών. Το quote δεν ισχύει (το disposing γίνεται κατα σύμβαση, όχι αυτόματα), αλλά και τα συμπεράσματά σου εδώ είναι λάθος. Στον finalizer δεν επιτρέπεται να κάνεις ο,τιδήποτε με αυτά τα αντικείμενα -- είχες την ευκαιρία σου μέσα στο Dispose() σου αλλά την έχασες (και θεωρητικά έχουν κι αυτά finalizer και θα γίνουν finalized μόνα τους). Για κάθε DataTable (επαναλαμβάνω δε μιλάω για το visual component DataGridView) που μπορείς να φτιάξεις σε μία φόρμα, πρέπει να καλείται το Dispose χειροκίνητα; Και ΑΝ ναι πρέπει να γίνουν dispose και όλα τα DataColumn s ξεχωριστά; Ναι. Αλλά τα DataColumn δε χρειάζονται disposing γιατί κατα σύμβαση θα το κάνει ο DataTable που τα περικλείει. Μα η πλάκα είναι ότι δεν έχω καθόλου εμφανή leaks μέχρι στιγμής και η εφαρμογή παίζει 1 χρόνο τώρα. Δεν έχεις leaks γιατί eventually τρέχουν οι finalizers. Μέχρι τότε έχεις "προσωρινά" leaks αλλά δεν φαίνονται γιατί το σύστημα έχει αρκετούς πόρους. Θα μπορούσες όμως να φτιάξεις μια class που κάνει κάτι παρόμοιο (π.χ. ανοίγει file handles) και να δημιουργείς φόρτο χωρίς να κάνεις dispose -- θα φανεί το τι συμβαίνει με κάποιο resource monitoring πρόγραμμα. 2
παπι Δημοσ. 14 Οκτωβρίου 2012 Δημοσ. 14 Οκτωβρίου 2012 Αυτο το "skill" σου να γραφεις ευκολοδιαβαστα σεντονια... +1 οπως παντα
moukoublen Δημοσ. 14 Οκτωβρίου 2012 Μέλος Δημοσ. 14 Οκτωβρίου 2012 (επεξεργασμένο) Defacer ευχαριστώ πολύ για την λεπτομερέστατη επεξήγηση! Άρα, σε σχέση με τα DataTables, είναι καλό να τα κάνω dispose όταν η φόρμα κλείνει αλλά αν δε γίνει αυτό, όταν θα επιλέξει ο garbage collector να τα μαζέψει γιατί δεν υπάρχουν άλλες αναφορές σε αυτά (όπως έτσι κι αλλιώς γίνεται), γιατί η φόρμα που τα περιείχε έχει κλείσει και δεν υπάρχει κάποια εκτός τις φόρμας αναφορά σε αυτά,ο finalizer τους θα "καθαρίσει" έστω τους unmanaged πόρους (αν αυτοί υπάρχουν - που ίσως κάτι υπάρχει από το MarshalByValueComponent - θα κοιτάξω στον κώδικα του .net) καλώντας το Dispose(false) και θα αφήσει τα managed να πάρουν το δικό τους δρόμο. Τώρα και συγκεκριμένα με τα DataTables πάλι (ελπίζω να μην το ψειρίζω) είναι το ίδιο σημαντικό όπως π.χ. με ένα stream, να γίνονται Dispose ακριβώς τη στιγμή που τελειώνεις μαζί τους (δηλαδή τα Dispose των DataTables θα τα βάλω στα κλεισίματα των φορμών απλά γενικά ρωτάω); Γιατί από στο συμπέρασμα που βγάζω από τα παραπάνω είναι πως δεν είναι το ίδιο σημαντικό. Δεν συζητώ γενικά για τα Disposable objects. Γιατί η λογική είναι (και είναι πολύ σημαντικό) "Αν το Dispose υποστηρίζεται τότε (όταν τελειώσεις) κάλεσε το !" EDIT: Τελικά όντως η Form στο Dispose μιας και εκτελείτε και η Dispose του Control που είναι η βάση της, κάνει dispose ένα- ένα όλα τα control απο τον controlcontainer. > // Control.Dispose(bool disposing) ... // ... if (controlsCollection != null) { // PERFNOTE: This is more efficient than using Foreach. Foreach // forces the creation of an array subset enum each time we // enumerate for(int i = 0; i < controlsCollection.Count; i++) { Control ctl = controlsCollection[i]; ctl.parent = null; ctl.Dispose(); } Properties.SetObject(PropControlsCollection, null); } Control.Dispose(bool disposing) > protected override void Dispose(bool disposing) { if (GetState(STATE_OWNCTLBRUSH)) { object backBrush = Properties.GetObject(PropBackBrush); if (backBrush != null) { IntPtr p = (IntPtr)backBrush; if (p != IntPtr.Zero) { SafeNativeMethods.DeleteObject(new HandleRef(this, p)); } Properties.SetObject(PropBackBrush, null); } } //set reflectparent = null regardless of whether we are in the finalizer thread or not. UpdateReflectParent(false); if (disposing) { if (GetState(STATE_DISPOSING)) { return; } if (GetState(STATE_CREATINGHANDLE)) { throw new InvalidOperationException(SR.GetString(SR.ClosingWhileCreatingHandle, "Dispose")); // I imagine most subclasses will get themselves in a half disposed state // if this exception is thrown, but things will be equally broken if we ignore this error, // and this way at least the user knows what they did wrong. } SetState(STATE_DISPOSING, true); this.SuspendLayout(); try { DisposeAxControls(); ContextMenu contextMenu = (ContextMenu)Properties.GetObject(PropContextMenu); if (contextMenu != null) { contextMenu.Disposed -= new EventHandler(DetachContextMenu); } ResetBindings(); if (IsHandleCreated) DestroyHandle(); if (parent != null) { parent.Controls.Remove(this); } ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); if (controlsCollection != null) { // PERFNOTE: This is more efficient than using Foreach. Foreach // forces the creation of an array subset enum each time we // enumerate for(int i = 0; i < controlsCollection.Count; i++) { Control ctl = controlsCollection[i]; ctl.parent = null; ctl.Dispose(); } Properties.SetObject(PropControlsCollection, null); } base.Dispose(disposing); } finally { this.ResumeLayout(false); SetState(STATE_DISPOSING, false); SetState(STATE_DISPOSED, true); } } else { #if FINALIZATION_WATCH if (!GetState(STATE_DISPOSED)) { Debug.Fail("Control of type '" + GetType().FullName +"' is being finalized that wasn't disposed\n" + allocationSite); } #endif // This same post is done in NativeWindow's finalize method, so if you change // it, change it there too. // if (window != null) { window.ForceExitMessageLoop(); } base.Dispose(disposing); } } // Package scope to allow AxHost to override. // internal virtual void DisposeAxControls() { ControlCollection controlsCollection = (ControlCollection)Properties.GetObject(PropControlsCollection); if (controlsCollection != null) { // PERFNOTE: This is more efficient than using Foreach. Foreach // forces the creation of an array subset enum each time we // enumerate for(int i = 0; i < controlsCollection.Count; i++) { controlsCollection[i].DisposeAxControls(); } } } Επεξ/σία 14 Οκτωβρίου 2012 από moukoublen
moukoublen Δημοσ. 14 Οκτωβρίου 2012 Μέλος Δημοσ. 14 Οκτωβρίου 2012 Λοιπόν έκανα κάποιες αναζητήσεις για το θέμα των DataSets και DataTables και διαβάζοντας εδώ και εδώ φαίνεται (συγκεκριμένα για αυτά τα δύο) να μην έχει ιδιαίτερη σημασία το Dispose().
geo1st487 Δημοσ. 18 Οκτωβρίου 2012 Δημοσ. 18 Οκτωβρίου 2012 Βεβαίως και έχει > class MyClass { ~MyClass() { Console.WriteLine("destructor"); } } static class Program { static void Main() { MyClass c = new MyClass(); } } Γιατι οταν τρεχω τον κωδικα δεν εκτελειται η ~MyClass() ?
albNik Δημοσ. 18 Οκτωβρίου 2012 Δημοσ. 18 Οκτωβρίου 2012 Γιατι οταν τρέχω τον κώδικα δεν εκτελείται η ~MyClass() ? Site: Destructors (C# programming guide) Η ~MyClass είναι ισοδύναμη με > protected override void Finalize() { try { Console.WriteLine("destructor"); } finally { base.Finalize(); } } Μήπως το κάνεις debug me break? Η διπλό κλικ το exe και δεν προλαβαίνεις να το δεις?
geo1st487 Δημοσ. 18 Οκτωβρίου 2012 Δημοσ. 18 Οκτωβρίου 2012 albNik δεν το κανω debug με break και στο τελος της Main εχω βαλει Console.ReadKey(); για να κραταει το παραθυρο ανοικτο. Επισης απο το link που εδωσες ετρεξα τον παρακατω κωδικα ο οποιος δεν εμφανιζει τιποτα στο output window > class First { ~First() { System.Diagnostics.Trace.WriteLine("First's destructor is called."); } } class Second : First { ~Second() { System.Diagnostics.Trace.WriteLine("Second's destructor is called."); } } class Third : Second { ~Third() { System.Diagnostics.Trace.WriteLine("Third's destructor is called."); } } class TestDestructors { static void Main() { Third t = new Third(); } } /* Output (to VS Output Window): Third's destructor is called. Second's destructor is called. First's destructor is called. */
albNik Δημοσ. 18 Οκτωβρίου 2012 Δημοσ. 18 Οκτωβρίου 2012 albNik δεν το κανω debug με break και στο τελος της Main εχω βαλει Console.ReadKey();. Το ReadKey() υποψιαζόμουν. Διέγραψε το. Τρεξτο απο Console (cmd) για να μην κλείσει. Απο Visual Studio δεν κλείνει , Δείχνει το "Press any key to continue..."
geo1st487 Δημοσ. 18 Οκτωβρίου 2012 Δημοσ. 18 Οκτωβρίου 2012 Το ReadKey() υποψιαζόμουν. Διέγραψε το. Τρεξτο απο Console (cmd) για να μην κλείσει. Απο Visual Studio δεν κλείνει , Δείχνει το "Press any key to continue..." Ετρεξα το exe και εμφανιζει το παραθυρο της κονσολας κενο. Αν το τρεξω μεσα απο το Visual Studio θα κλεισει χωρις το ReadKey() και δεν εμφανιζει "Press any key to continue..." Σε εσενα εμφανιζει αποτελεσματα στο Output window?
Προτεινόμενες αναρτήσεις
Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε
Πρέπει να είστε μέλος για να αφήσετε σχόλιο
Δημιουργία λογαριασμού
Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!
Δημιουργία νέου λογαριασμούΣύνδεση
Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.
Συνδεθείτε τώρα