[.NET] Disposable pattern

Pagina: 1
Acties:

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-05 01:02
Ik las net deze post op Brian Buttons Blog:

klik

Hij heeft het hier over het Disposable pattern dat vastgelegd is volgens de .NET guidelines.
Even ter informatie: als een class disposable is, dan moet je in principe zelf de 'Dispose()' method aanroepen om het object dat je ginstantieerd hebt, 'op te kuisen' als je het niet meer nodig hebt. Op die manier kan je dus resources gaan vrijgeven.
Nu is zijn stelling dat je, voor dergelijke classes, een exceptie of een assert zou moeten plaatsen in de Finalizer van de class. (De Finalizer is de 'destructor' die aangeroepen wordt door de runtime).
Nu is zijn stelling dat de Finalizer een exceptie zou moeten gooien als de Dispose niet aangeroepen werd.

Wat vinden jullie van die stelling ? Op zich vind ik het wel een goed idee, aangezien je op die manier 'gedwongen' wordt om Dispose aan te roepen.

https://fgheysels.github.io/


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Misschien niet helemaal on-topic, maar ik vroeg me af waarom je zelf je objecten wil vrijgeven en niet door de GC?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • whoami
  • Registratie: December 2000
  • Laatst online: 09-05 01:02
Kaassoevlee schreef op vrijdag 25 maart 2005 @ 09:40:
Misschien niet helemaal on-topic, maar ik vroeg me af waarom je zelf je objecten wil vrijgeven en niet door de GC?
Omdat je niet weet wanneer de GC juist jouw object gaat vrijgeven.
Stom voorbeeld: Stel dat jouw object een file open heeft staan, dan ga je toch die lock direct willen vrijgeven?

https://fgheysels.github.io/


Verwijderd

Ik snap het even niet... z'n idee is me wel duidelijk, denk ik. Gewoon enforcen dat Dispose wordt aangeroepen in de finalizer... maar zo'n finalizer schrijf je toch ook gewoon zelf, niet? Waarom roep je dan niet gewoon in de finalizer zelf even dispose aan als dat nog niet gebeurd is, in plaats van dat je een exception gooit waar een gebruiker van je class weer naar zal kunnen zoeken waar hij vandaan komt? Da's ook nog eens extra verwarrend omdat de exception wordt gegooid vanaf de GC thread EN pas nadat je eigenlijk 'klaar' bent met je object, dus het zal zeker niet meteen duidelijk zijn wat er mis gaat.

Zijn oplossing is IMO een beetje afwijken van het standaard patroon wat we bij veel platformen met GC zien en ik denk dat je er bij de gebruikers van je klasses geen vrienden mee maakt. Mensen zijn zo langzamerhand gewend aan het principe dat de GC alles opruimt en dat ze zelf hun rommel niet meer expliciet hoeven op te ruimen... daarvoor is de finalizer toch eigenlijk ook bedoeld? Je zou beter het nut van een Dispose method aan de kaak kunnen stellen die je als 'eindgebruiker' van een class nog zelf expliciet moet aanroepen.

[edit]
Stom voorbeeld: Stel dat jouw object een file open heeft staan, dan ga je toch die lock direct willen vrijgeven?
Om jou voorbeeld te gebruiken...

Als het object het bestand gebruikt voor een eenmalige IO operatie in een method, dan zal je het bestand willen sluiten aan het einde van die method.

Als het object het bestand continue nodig heeft en het de hele tijd open houdt, dan kun je het net zo goed in de finalizer afsluiten in plaats van in een expliciete dispose method als je het object niet meer nodig hebt (maw. die extra seconde voor GC maakt dan ook niks meer uit).

Mijn vraag is, wat moet je nog met een object als je er Dispose op aan hebt geroepen? Als je antwoord 'niks' is, dan is het nut van een expliciete Dispose eigenlijk het punt van de discussie hier, niet? :)

[ Voor 33% gewijzigd door Verwijderd op 25-03-2005 10:00 ]


  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 01-05 19:09

pjvandesande

GC.Collect(head);

Ik denk dat dit een goed idee is van hem. Gewoon een Debug.WriteLine die het duidelijk maakt dat je Dispose niet hebt aangeroepen.

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-05 01:02
Verwijderd schreef op vrijdag 25 maart 2005 @ 09:50:
Ik snap het even niet... z'n idee is me wel duidelijk, denk ik. Gewoon enforcen dat Dispose wordt aangeroepen in de finalizer... maar zo'n finalizer schrijf je toch ook gewoon zelf, niet? Waarom roep je dan niet gewoon in de finalizer zelf even dispose aan als dat nog niet gebeurd is, in plaats van dat je een exception gooit waar een gebruiker van je class weer naar zal kunnen zoeken waar hij vandaan komt? Da's ook nog eens extra verwarrend omdat de exception wordt gegooid vanaf de GC thread EN pas nadat je eigenlijk 'klaar' bent met je object, dus het zal zeker niet meteen duidelijk zijn wat er mis gaat.
Omdat, als je een Disposable class hebt, je eigenlijk ook wilt zeggen dat je die Dispose 'moet' aanroepen.
Als de GC de finalizer aanroept, dan worden je resources eigenlijk laat vrijgegeven, en in zo'n geval mag je eigenlijk niet vergeten om de dispose aan te roepen.
Zijn oplossing is IMO een beetje afwijken van het standaard patroon wat we bij veel platformen met GC zien en ik denk dat je er bij de gebruikers van je klasses geen vrienden mee maakt. Mensen zijn zo langzamerhand gewend aan het principe dat de GC alles opruimt en dat ze zelf hun rommel niet meer expliciet hoeven op te ruimen... daarvoor is de finalizer toch eigenlijk ook bedoeld? Je zou beter het nut van een Dispose method aan de kaak kunnen stellen die je als 'eindgebruiker' van een class nog zelf expliciet moet aanroepen.
Dispose heeft wel degelijk nut, en het getuigt van een luiheid van de programmeur die met jouw classes werkt als hij Dispose niet aanroept.
Waarom maak je je class dan anders disposable. Het is gewoon niet netjes om bepaalde resources langer 'vast te houden' dan nodig is. (Zie m'n voorbeeld van die file).
Je weet gewoon niet wanneer de GC z'n werk doet, dus het is niet netjes om daar in ieder geval gewoon maar op te vertrouwen. Dat is een beetje het probleem dat we zo langzamerhand zien: Oh, de GC doet dat wel voor mij, dus ik hoef me er niet druk over te maken. Nee, zo werkt het niet.
De Dispose method is er om ook 'unmanaged resources' zoals files etc... te kunnen opruimen.
Use this method to close or release unmanaged resources such as files, streams, and handles held by an instance of the class that implements this interface. This method is, by convention, used for all tasks associated with freeing resources held by an object, or preparing an object for reuse.
questa schreef op vrijdag 25 maart 2005 @ 09:54:
Ik denk dat dit een goed idee is van hem. Gewoon een Debug.WriteLine die het duidelijk maakt dat je Dispose niet hebt aangeroepen.
Ik zou eerder een Assert doen, op die manier wordt je er veel beter op gericht.
(Je hoeft dus geen exception te gooien, als je het via een assert doet, dan wordt die Assert ook niet uitgevoerd bij een release build van je app).

[ Voor 11% gewijzigd door whoami op 25-03-2005 10:01 ]

https://fgheysels.github.io/


Verwijderd

Hmm... ik weet het niet hoor. Als de infrastructuur voor GC er ligt, met finalizers, waarom zou je daar dan geen gebruik van maken om het de gebruikers van makkelijker te maken? Ok, je weet niet wanneer de GC z'n werk doet, maar het zal geen uren duren.

Daarnaast geef je zelf al aan dat je met een Dispose method moet vertrouwen op de gebruiker van je classes... en je geeft zelf al aan hoe lui programmeurs kunnen zijn... waarom neem je dit risico dan niet weg door de finalizer te gebruiken?

Kijk, als je het zo aanpakt als bij een Windows form, dat een Dispose weer impliciet wordt aangeroepen door het Application principe wanneer dat nodig is, dan heeft het misschien wel nut, maar voor je eigen klasses lijkt me de finalizer toch de plek om je rotzooi op te ruimen, eventueel met een (voor de gebruiker) impliciete aanroep van je Dispose method.

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-05 01:02
Verwijderd schreef op vrijdag 25 maart 2005 @ 10:06:
Hmm... ik weet het niet hoor. Als de infrastructuur voor GC er ligt, met finalizers, waarom zou je daar dan geen gebruik van maken om het de gebruikers van makkelijker te maken? Ok, je weet niet wanneer de GC z'n werk doet, maar het zal geen uren duren.
Omdat je niet weet wanneer de GC inkicked.
Omdat je die resources NU wil vrijgeven
Omdat Finalizen duur is
Omdat jij lui bent.

De architecten van MS die zich daar over gebogen hebben, weten echt wel waar ze het over hebben, en weten echt wel wat ze doen hoor.
Daarnaast geef je zelf al aan dat je met een Dispose method moet vertrouwen op de gebruiker van je classes... en je geeft zelf al aan hoe lui programmeurs kunnen zijn... waarom neem je dit risico dan niet weg door de finalizer te gebruiken?
Zie boven.
Trouwens, als je het using statement gebruikt, dan wordt dispose zelf ook wel aangeroepen.
Class instances often encapsulate control over resources that are not managed by the runtime, such as window handles (HWND), database connections, and so on. Therefore, you should provide both an explicit and an implicit way to free those resources. Provide implicit control by implementing the protected Finalize Method on an object (destructor syntax in C# and the Managed Extensions for C++). The garbage collector calls this method at some point after there are no longer any valid references to the object.
In some cases, you might want to provide programmers using an object with the ability to explicitly release these external resources before the garbage collector frees the object. If an external resource is scarce or expensive, better performance can be achieved if the programmer explicitly releases resources when they are no longer being used. To provide explicit control, implement the Dispose method provided by the IDisposable Interface. The consumer of the object should call this method when it is done using the object. Dispose can be called even if other references to the object are alive.
Note that even when you provide explicit control by way of Dispose, you should provide implicit cleanup using the Finalize method. Finalize provides a backup to prevent resources from permanently leaking if the programmer fails to call Dispose.
Trouwens, het gaat er niet om om een exception te gooien in de finalizer, het gaat erom om een Assert daar te zetten die geraised wordt als dispose niet aangeroepen werd.
Op die manier zie je het als programmeur in je debug-build, dat je een dispose bent vergeten aan te roepen.
Niets weerhoudt je ervan om in je finalizer daarna alsnog het object vrij te geven.

[ Voor 47% gewijzigd door whoami op 25-03-2005 10:16 ]

https://fgheysels.github.io/


Verwijderd

Ok, dan... niet om afbreuk te doen aan de experts van Microsoft (ik zou niet durven >:) ;) ), maar dit wat je hier aanhaalt lijkt me een typisch geval van over-engineering. Volgens Brian button zou je (volgens de experts die het Disposable principe hebben bedacht) dispose dus zo moeten gebruiken:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public class BuildProcess : IDisposable
    {
        ~BuildProcess()
        {
            Dispose(false);
        }

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

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
            }
        }
    }


Dus werkt de finalizer als 'fail-safe' voor het geval dat de dispose niet is aangeroepen. Als ik kijk naar de GC.SuppressFinalize(this); call, dan zie je dat dit toch eigenlijk een beetje houtje-touwtje werk is... Om iets op te ruimen (Dispose) moet ik eerst de stofzuiger (de GC) uit zetten zodat ik het zelf kan doen... hmmm...

offtopic:
Daarnaast geef je aan dat met bv. het using statement de dispose method al impliciet wordt aangeroepen als het using block wordt afgesloten... Los ervan dat dit een mooi voorbeeld is van hoe de experts language constructs en specifieke class interfaces door elkaar gebruiken (puristen discussie, maar *yuck*), geeft dit wel te denken over het nut van de finalizer in een Disposable object... waar is hij dan nog goed voor?


Ok, dan gaan we de finalizer maar gebruiken om te checken of Dispose is aangeroepen (dus de stofzuiger gebruiker om te kijken of er is opgeruimd!)... Dat ze dit met een assert doen vind ik wel een nette oplossing, maar ik blijf erbij dat het voor gebruikers op z'n minst 'wennen' wordt om een assert die door de GC thead wordt gedaan te gaan debuggen... en voor beginnende gebruikers lijkt het me ook een moelijk te verkopen concept.

Het gevoel dat ik erbij krijg is dat ze het zelf ook niet meer weten met hun GC concept. Ik denk dat het zo prima is. Voor beginnende gebruikers gebeurt alles achter de schermen zonder dat je er last van hebt en de mensen die het echt wat interesseert kiezen zelf wel hoe ze hun rotzooi opruimen (en zetten desgewenst zelf wel een assert in de finalizer).

Maarja, ik ben dan ook geen expert maar gewoon lui ;)

[ Voor 5% gewijzigd door Verwijderd op 25-03-2005 10:30 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 09-05 01:02
Verwijderd schreef op vrijdag 25 maart 2005 @ 10:26:
Ok, dan... niet om afbreuk te doen aan de experts van Microsoft (ik zou niet durven >:) ;) ), maar dit wat je hier aanhaalt lijkt me een typisch geval van over-engineering. Volgens Brian button zou je (volgens de experts die het Disposable principe hebben bedacht) dispose dus zo moeten gebruiken:


Dus werkt de finalizer als 'fail-safe' voor het geval dat de dispose niet is aangeroepen. Als ik kijk naar de GC.SuppressFinalize(this); call, dan zie je dat dit toch eigenlijk een beetje houtje-touwtje werk is... Om iets op te ruimen (Dispose) moet ik eerst de stofzuiger (de GC) uit zetten zodat ik het zelf kan doen... hmmm...
Lees eens het betreffende MSDN artikel, zodanig dat je weet WAAROM je dat moet doen.
geeft dit wel te denken over het nut van de finalizer in een Disposable object... waar is hij dan nog goed voor?
Lees eens het betreffende MSDN artikel.
Ok, dan gaan we de finalizer maar gebruiken om te checken of Dispose is aangeroepen... Dat ze dit met een assert doen vind ik wel een nette oplossing, maar ik blijf erbij dat het voor gebruikers op z'n minst 'wennen' wordt om een assert die door de GC thead wordt gedaan te gaan debuggen... en voor beginnende gebruikers lijkt het me ook een moelijk te verkopen concept.
Gebruiker is in dit geval wel 'programmeur'.
Het gevoel dat ik erbij krijg is dat ze het zelf ook niet meer weten met hun GC concept. Ik denk dat het zo prima is. Voor beginnende gebruikers gebeurt alles achter de schermen zonder dat je er last van hebt en de mensen die het echt wat interesseert kiezen zelf wel hoe ze hun rotzooi opruimen (en zetten desgewenst zelf wel een assert in de finalizer).
Zoals ik al zei: gebruiker is in dit geval progammeur, en een programmeur moet nog altijd weten wat hij doet.

Trouwens, dit topic ging niet over het nut van Dispose.

https://fgheysels.github.io/


Verwijderd

Zou altijd nog moeten weten wat hij doet... Maar goed, da's weer een andere discussie.

Ik denk dat de discussie over het nut van dispose / GC eigenlijk de basis is voor een discussie over Brian's idee. Als iedereen overtuigt was van van het nut van dispose en iedereen er goed mee omging, dan zou gabage collection helemaal niet nodig zijn en was Brian nooit met de discussie begonnen.

Maar goed, ik heb m'n mening gegeven en jij hebt me verteld dat ik maar MSDN artikelen moet gaan lezen, dus ik denk dat ik het hierbij hou.

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-05 01:02
Verwijderd schreef op vrijdag 25 maart 2005 @ 10:49:
Zou altijd nog moeten weten wat hij doet... Maar goed, da's weer een andere discussie.
Begin niet he, je bent geen onwetende gebruiker maar een programmeur. En die weet nog altijd wat hij doet; althans dat hoop ik toch.
Ik denk dat de discussie over het nut van dispose / GC eigenlijk de basis is voor een discussie over Brian's idee. Als iedereen overtuigt was van van het nut van dispose en iedereen er goed mee omging, dan zou gabage collection helemaal niet nodig zijn en was Brian nooit met de discussie begonnen.
Je kan gewoon niet altijd wachten op de GC.
Je wil dat gewoon niet.
En het is ook niet nodig om iedere class 'Disposable' te maken.
De GC is gewoon een hulpmiddel die je wat werk uit handen neemt, maar dat neemt niet weg dat je nog altidj moet weten wat je doet, en dat je in bepaalde gevallen nog altijd zelf wilt bepalen wanneer je iets wilt vrijgeven.
Maar goed, ik heb m'n mening gegeven en jij hebt me verteld dat ik maar MSDN artikelen moet gaan lezen, dus ik denk dat ik het hierbij hou.
Dat moet je idd gaan doen.

https://fgheysels.github.io/


Verwijderd

whoami schreef op vrijdag 25 maart 2005 @ 10:59:
[...]

Je kan gewoon niet altijd wachten op de GC.
Je wil dat gewoon niet.
En het is ook niet nodig om iedere class 'Disposable' te maken.
De GC is gewoon een hulpmiddel die je wat werk uit handen neemt, maar dat neemt niet weg dat je nog altidj moet weten wat je doet, en dat je in bepaalde gevallen nog altijd zelf wilt bepalen wanneer je iets wilt vrijgeven.

[...]
Als je als ontwikkelaar niet 100% zeker weet wanneer je resources worden vrijgegeven kun je imho nooit een solide applicatie bouwen.

Dat is precies de reden dat ik in VB6 dit pattern ook al op vrijwel identieke wijze implementeerde.

edit:
Even voor de volledigheid: de Dispose wordt top-level aangeroepen en in de implementatie ruimt elk object z'n eigen objectreferences op door eerst de Dispose aan te roepen op dat object en het daarna weg te donderen. In de terminator van een object de Dispose aanroepen geeft dus aan dat de programmeur het niet helemaal begrepen heeft.
De methode om je eigen werk (of dat van collega's) te testen door een assert te doen in de finalize als de Dispose niet is aangeroepen lijkt mij niet meer dan normaal. (Tenzij je jezelf of je werk niet serieus neemt natuurlijk }) )


Ook al heeft .NET GC zie ik niet in waarom ik af zou wijken van deze manier van werken. Aangezien de GC resources pas gaat vrijgeven als 'ie er tijd voor krijgt zou je weleens compleet onnodig een volledige object-tree in geheugen kunnen houden...

(Natuurlijk is dat geen probleem voor programmeurs, aangezien de klant klakkeloos akkoord gaat met de eis dat er minimaal 1GiB intern geheugen in het pc'tje moet zitten om de applicatie te kunnen draaien |:( )

[ Voor 23% gewijzigd door Verwijderd op 25-03-2005 11:37 ]


  • pjonk
  • Registratie: November 2000
  • Laatst online: 29-12-2025
Als je een beetje serieuze applicatie bouwt met Database connecties, FileHandles en grote datastructuren moet je dit soort resources zelf vrijgeven. De GC is handig zodat je niet bijv. een tijdelijke gealloceerde array steeds zelf moet weggooien (zoals in C++), maar is geen reden om je niet meer over memory management te bekommeren.

It’s nice to be important but it’s more important to be nice


  • Korben
  • Registratie: Januari 2001
  • Laatst online: 14-11-2025

Korben

() => {};

Ik vind het een goede stelling, omdat het je dwingt om weer 'netjes' te gaan programmeren. Ik merk zelf ook dat sinds ik C# gebruik, ik steeds slordiger omga met geheugen, dus ik vind het wel een fijn idee om in de finalizer exceptions te laten throwen als de klasse disposable is. Ik vind het zelf ook wel een fijn idee om het geheugen zelf weer onder controle te hebben, en inderdaad niet gigantische hoeveelheden geheugen gealloceerd hebben die allang niet meer nodig zijn.

.oisyn: Échte programmeurs haten PHP met een passie. Ben jij soms geen echte programmeur?

Pagina: 1