[.NET] Using statement

Pagina: 1
Acties:

Onderwerpen


  • keesdewit
  • Registratie: December 2003
  • Laatst online: 19-06 20:46
Dit topic gaat over het gebruik van het using statement op idisposeable objecten in .NET

Ik probeer op ieder idisposeable object een Using statement te gebruiken, echter vraag ik me af of ik dat in de volgende situatie correct doe:

Visual Basic .NET:
1
2
3
4
5
6
7
8
Dim objLabel as Label = Nothing

Using objLabel
   For Each objRow as DataGridViewRow In grdGridView
      objLabel = objRow.FindControl("lblLabel")
      objLabel.Text &= "Voeg iets toe"
   Next
End Using


In de voorbeelden die ik op internet zie staan gaat men er vanuit dat het object binnen het Using statement wordt geinitialiseerd: Using objLabel As Label = objRow.FindControl("lblLabel")

Heeft dit invloed op het gebruik van het Using statement? Worden mijn objecten op de juiste manier gedisposed? Is het nodig om bijvoorbeeld gedeclareerde controls te disposen en zo ja, waar is het precies goed voor en wat voor effect krijg je als je dat niet doet? Is het beter om binnen een routine in bijvoorbeeld een for loop het object te hergebuiken of is het beter om het using blok binnen de for next loop te zetten, zodat hij bij iedere loop het object initialiseerd en disposed. Ik heb er nu voor gekozen om de Using statements om de hele routine te plaatsen. De objecten binnen deze routinen hebben geen referenties buiten deze routine.

  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 23:29

mulder

ik spuug op het trottoir

Using is helemaal niet bedoeld voor Labels e.d., het is bedoeld voor objecten die unmanaged zaken gebruiken, zoals een File. Using zorgt er voor dat er niet gewacht wordt op de garbage collector om objecten te sluiten en weg te gooien.

oogjes open, snaveltjes dicht


  • Amras
  • Registratie: Januari 2003
  • Laatst online: 16-09 20:15
mulder schreef op woensdag 22 september 2010 @ 10:49:
Using zorgt er voor dat er niet gewacht wordt op de garbage collector om objecten te sluiten en weg te gooien.
Een using statement doet toch niet meer dan het aanroepen van de Dispose methode van een IDisposable (ook als er bijv. een exception optreedt)? Dat betekent niet dat de garbage collector niet meer nodig is om het object op te ruimen.

  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 23:29

mulder

ik spuug op het trottoir

Yup ;)

oogjes open, snaveltjes dicht


  • keesdewit
  • Registratie: December 2003
  • Laatst online: 19-06 20:46
@Mulder: Weet jij dan waarom System.Web.UI.WebControls idisposable implementeert? Met wat voor reden zou je dan wel een dergelijk object disposen? Ik lees inderdaad op de MS website dat managed resources automatisch worden opgepakt door de GC. Heeft het enig voordeel als ik managed resources wel zelf dispose? (bijvoorbeeld op applicaties met een hoge workload?)

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
@TS: Gebruik a.u.b. de [code=taal] tags, dan is je code een stuk beter leesbaar.

Volgens mij heb je, wat betreft using, de klok wel horen luiden, maar weet je niet waar de klepel is ;)

using is bedoeld voor objecten die IDisposable gebruiken, maar dat zegt niet dat je meteen overal waar je een IDisposable object hebt using moet gebruiken.

Je moet alleen using gebruiken, als je wil dat alle resources van het object vrijgegeven worden op het moment dat je de scope van het using blok verlaat. Nu heb je het in je voorbeeld over een label waar je iets mee wil doen, maar als je uit de scope van de using gaat, wil je helemaal niet dat het object vrij gegeven word, immers moet het label nog steeds gewoon op je form getoond blijven.

Using moet je bijvoorbeeld gebruiken als je een file wil openen om te lezen/schrijven, waarna je hem meteen weer sluit. Iets als het volgende
C#:
1
2
3
4
using(StreamReader reader = new StreamReader(myFilePath))
{
    reader.WriteLine("BLAAT");
}

Op het moment dat je uit de scope van het using blok gaat, al dan niet door een Exception, weet je zeker dat alle resources van de StreamReader vrij gegeven worden. Bij een file is het belangrijk dat je dat d.m.v. Dispose doet, aangezien je een File Handle niet langer vast wil houden dan noodzakelijk, en je weet niet exact wanneer de GC langs komt om het object op te ruimen. Destructors zijn namelijk niet deterministisch zoals in bijvoorbeeld C++

edit:
Een label implementeerd IDisposable omdat er blijkbaar ook resources gebruikt worden die vrij gegeven moeten worden. Dat betekend echter niet meteen dat jij die aan moet roepen. De Dispose methode moet natuurlijk pas aangeroepen worden op het moment dat het object niet meer nodig is. In dit geval zal dat door de code van je Form gedaan worden.

[ Voor 21% gewijzigd door Woy op 22-09-2010 11:15 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Vedett.
  • Registratie: November 2005
  • Laatst online: 17-09 13:49
@Amras: Klopt volledig. De verwarring is denk ik ontstaan omdat in sommige gevallen in de Dispose methode GC.SuppressFinalize(this); wordt aangeroepen. Dit heeft invloed op de garbage collection, maar voorkomt uiteraard de garbage collection niet. Het zegt gewoon de de Finalizer niet meer moet opgeroepen worden waardoor het object sneller uit het geheugen kan gehaald worden, omdat finalizers pas in een volgende ronde van de Garbage Collection worden uitgevoerd.

@keesdewit:
Dit is inderdaad helemaal verkeerd. Je moet niet zomaar using gebruiken op elk IDisposable object. Je moet kijken of je het object nog nodig hebt! En ik vermoed dat dit hier het geval is. Wat je nu gaat doen is Dispose oproepen op de laatste label die je in de For Each hebt gevonden. Eigenlijk is using iets dat de compiler u beschikbaar stelt om het programmeren wat aangenamer te maken

De C# compiler maakt hier het volgende van (niet getest):

C#:
1
2
3
4
5
6
7
8
9
10
11
 

Label objLabel = null;

try{
   foreach(.....){
       objLabel = objRow.FindControl("lblLabel");
   }
}finally{
   if(null != objLabel) ((IDisposable)objLabel).Dispose();
}

  • keesdewit
  • Registratie: December 2003
  • Laatst online: 19-06 20:46
@Woy: Ik gebruik het Using statement ook op die manier, de controls waar ik het op gebruik hebben geen waarde buiten het Using block. Het control komt echter wel op het scherm, ookal gebruik ik een dispose. Waarom zegt MS dit "Managed resources are disposed of by the .NET Framework garbage collector (GC) without any extra coding on your part. You do not need a Using block for managed resources" als ze toch een IDisposable implementatie hebben gedaan bij de diversen controls?

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
keesdewit schreef op woensdag 22 september 2010 @ 11:18:
@Woy: Ik gebruik het Using statement ook op die manier, de controls waar ik het op gebruik hebben geen waarde buiten het Using block. Het control komt echter wel op het scherm, ookal gebruik ik een dispose. Waarom zegt MS dit "Managed resources are disposed of by the .NET Framework garbage collector (GC) without any extra coding on your part. You do not need a Using block for managed resources" als ze toch een IDisposable implementatie hebben gedaan bij de diversen controls?
Je loopt door je labels heen, en dus wil je eigenlijk niet dat ze gedisposed worden nadat je er doorheen gelopen hebt, de labels ( niet de reference naar je label ) hebben immers een langere levensduur. Je voorbeeld is dus niet het beoogde gebruik van using, en al helemaal niet omdat je de reference naar het object die uiteindelijk gedisposed word telkens aanpast. Dat is ook de reden dat je normaal gesproken ook de initialisatie van het object in de using doet. Door het using blok garandeer je namelijk de lifetime van het object, en imiteer je het deterministische gedrag van destructors in C++.

Waarom MS er voor gekozen heeft om de controls de IDisposable interface te laten implementeren weet ik niet. Het zou kunnen dat de controls intern Native resources gebruiken die weer vrijgegeven moeten worden. Maar het eigendom van de Controls ligt niet bij jouw code, maar bij de code van het Form/Parent Control, dus die is ook verantwoordelijk om de Dispose methode aan te roepen op het moment dat het object niet meer nodig is, en dus Disposed kan worden.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Het Label object wat wordt teruggegeven door de FindControl methode is niet een nieuw object wat los staat van de label die op je form staat. Het is diezelfde Label! Als je nu daar meteen Dispose op aanroept zal die label dus verdwijnen. Dat lijkt me niet logisch omdat je vlak daarvoor de Text property veranderd. Waarom zou je dat doen als je de label daarna meteen weg gooit?

Heb je de code eigenlijk wel geprobeerd? Want als het goed is vernietig je de label met deze code en zal hij dus niet meer zichtbaar zijn. Als dat het geval was had je dat vast wel gemeld in je start post neem ik aan, of dan was dat in ieder geval een indicatie dat het niet goed is wat je doet.

Mijn iRacing profiel


  • keesdewit
  • Registratie: December 2003
  • Laatst online: 19-06 20:46
@NickThissen: Ik heb de code zeker getest en het disposen van bijvoorbeeld een Label (uit FindControl verkregen) heeft geen gevolgen voor de output in je scherm, althans bij mij staat alles er keurig. Ik gebruik de gedeclareerde controls ook op de manier zoals jij aangeeft. Als ik bijvoorbeeld een property set van de voorgaande Label werkt dit inderdaad door op het scherm. Vreemd genoeg het disposen dus niet.

@Woy: Duidelijk verhaal. Mijn conslusie is dat het disposen inderdaad niet nodig is voor controls, echter is het nog een raadsel waarom er wel een dergelijke interface geimplementeerd is.

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
keesdewit schreef op woensdag 22 september 2010 @ 14:09:
@Woy: Duidelijk verhaal. Mijn conslusie is dat het disposen inderdaad niet nodig is voor controls, echter is het nog een raadsel waarom er wel een dergelijke interface geimplementeerd is.
De IDisposable interface word geinherit door System.ComponentModel.IComponent, dus al erg vroeg in de hiërarchie. Voor WinForms controls is het in ieder geval wel logisch dat ze IDisposable implementeren, want die hebben een Handle naar het Native component, en die moeten dus netjes opgeruimd worden.

Maar het zou ook best kunnen dat de WebForms controls ook een handle naar een of andere resource hebben, waardoor het logisch is dat de Disposable interface geïmplementeerd moet worden. Je moet ook niet denken dat je voor alle object die IDisposable implementeren je using moet gebruiken. Je moet alleen using gebruiken voor objecten die IDisposable implementeren, een "korte" levensduur hebben en waarvan je zelf de owner bent. ( Kort duid niet zozeer op de tijd dat een object leeft, maar meer op de scope waarin hij leeft ).

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • keesdewit
  • Registratie: December 2003
  • Laatst online: 19-06 20:46
@Woy: Nu is het helemaal duidelijk, bedankt.

  • yade
  • Registratie: Mei 2002
  • Laatst online: 16-07 13:47
Hmm, met de using wordt dispose aangeroepen, het object dus opgeruimd en zal dus ook van je form verdwijnen, maar in jouw geval bewaakt de using een null, waardoor er effectief niets gebeurd.

Kijk maar naar het verschil tussen:

C#:
1
2
3
4
5
6
            Button b = null;
            using (b)
            {
                b = button1;
                MessageBox.Show(b.Handle.ToString());
            }


en

C#:
1
2
3
4
5
            Button b = button1;
            using (b)
            {
                MessageBox.Show(b.Handle.ToString());
            }


Leer niet van alle bad practises. ;) Dit is slechts ter illustratie.

[ Voor 58% gewijzigd door yade op 22-09-2010 14:42 ]


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
yade schreef op woensdag 22 september 2010 @ 14:36:
Hmm, met de using wordt dispose aangeroepen, het object dus opgeruimd en zal dus ook van je form verdwijnen, maar in jouw geval bewaakt de using een null, waardoor er effectief niets gebeurd.
Je hebt gelijk, ik was in de veronderstelling dat misschien wel de Dispose van het laatste label gebruikt werd, maar dat is niet het geval
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Foo : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Foo");
    }
}

public class Bar : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Bar");
    }
}

IDisposable disposable = new Foo();
using(disposable)
{
    disposable = new Bar();
}

Zal "Foo" naar de console schrijven. Overigens krijg je daar ook niet voor niks een Compiler warning op
Possibly incorrect assignment to local 'disposable' which is the argument to a using or lock statement. The Dispose call or unlocking will happen on the original value of the local.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • DrDelete
  • Registratie: Oktober 2000
  • Laatst online: 21:44
Ook een gouden regel is dat als je een class instance variabele gebruikt, die IDisposable implementeert, jouw eigen class ook IDisposable moet implementeren. Jouw eigen IDisposable implementatie moet dan weer de Dispose method aanroepen van de class instance variabele. Dit is een pattern / implementatie die je op het internet kunt vinden.

Using aanroepen moet je pas doen als je een instance gebruikt die resource-intensief is (zoals dure database connecties, file handlers, etc...).
Pagina: 1