Reference Types en het Ref keyword visueel uitgelegd

 
08 december 2008

English version of this article

Een van de meest voorkomende vergissingen die ontwikkelaars maken bij het leren van .Net is het uit elkaar houden van Reference en Value Types en het doorgeven van een waarde “by reference”. In dit artikel zal ik proberen visueel uit te leggen wat het Ref keyword doet en wat zijn relatie is met ReferenceTypes vs. ValueTypes.

In C# zitten ValueTypes (structs) en ReferenceTypes (classes). Dit is een van de eerste dingen die je leert als je start met het leren van het .Net Foundations examen. Voor mijzelf was het erg hulpvol om wat achter de schermen gebeurd visueel te maken. Ik zal heel basic beginnen.

Allereest zul je het verschil moeten begrijpen tussen een code referentie en een daadwerkelijk object wat aanwezig is in het geheugen.

//code referentie
Persoon p;

Dit ziet er uit als onderstaand plaatje:

//code referentie gelinkt aan een daadwerkelijk object in geheugen
Persoon p = new Persoon(“Sjors Miltenburg”);

Dit ziet er uit als onderstaand plaatje:

Een object in het geheugen kan meerdere code referenties hebben die ernaar linken

Persoon p = new Persoon(“Sjors Miltenburg”);
Persoon p2 = p;

Dit ziet er uit als onderstaand plaatje:

Een object zal blijven leven (niet worden ge-garbage collect) zolang er een code referentie is die ernaar verwijst.

//Dit object wordt NIET garbage collected
Persoon p = new Persoon(“Sjors Miltenburg”);

Persoon p2 = p;
p = null;

//Dit object wordt WEL garbage collected
Persoon p = new Persoon(“Sjors Miltenburg”);
Persoon p2 = p;
p=null;
p2=null;

Het Ref keyword kan gebruikt worden wanneer je een method met inputvariabelen definieert, maar wat doet het nou eigenlijk? Om te begrijpen wat er gebeurd met het gebruik van het Ref keyword bekijken we eerst de situatie zonder Ref keyword.

public void VeranderPersoon(Persoon person){
//doe iets
}

//het aanroepen van de methode
Persoon p = new Persoon(“Sjors Miltenburg”);
VeranderPersoon(p);

Dit ziet er uit als onderstaand plaatje:

We zien dat we binnen de grenzen van de methode een nieuwe referentie krijgen naar het zelfde object. Dus als we in de methode iets doen zoals:

person=null;

dan zou de referentie p (buiten de methode) het oorspronkelijke object nog steeds in leven houden. Ook het toewijzen van de referentie aan een ander object heeft geen effect op de referentie “p” buiten de methode.

Wanneer we het Ref keyword gebruiken krijgen we de daadwerkelijke referentie zoals die ook geldt buiten de methode.

public void VeranderPersoon (ref Persoon person){
//doe iets
}

//het aanroepen van de methode
Persoon p = new Persoon(“Sjors Miltenburg”);
VeranderPersoon(ref p);

Dit ziet er uit als onderstaand plaatje:

Is dit ook mogelijk voor Value Types? Value Types reageren namelijk heel anders dan Reference Types als je probeert meerdere referenties naar hetzelfde object te maken.

ValueType vt = new ValueType(3, 5, 6);
ValueType vt2 = vt;

Dit resulteert in een gekopieerd object met de nieuw aangemaakte referentie (vt2) gelinkt aan de kopie.

Het gedrag wat hier boven wordt beschreven geldt ook voor het doorgeven van een Value Type aan methode zonder het Ref keyword.

Vanwege het gedrag van de ValueType (het niet toestaan van meerdere referenties) zou je misschien verwachten dat ook het doorgeven van een ValueType aan een methode met het Ref keyword een kopie zal opleveren. Dit is echter niet het geval.

public void VeranderValueType(ref ValueType vt2){
//doe iets
}

//het aanroepen van de methode
ValueType vt = new ValueType(3, 5, 6);
VeranderValueType(vt);

Dit ziet er uit als onderstaand plaatje:

Dit betekent dat het veranderen van een ValueType binnen de grenzen van de method ook zal resulteren in een veranderde ValueType buiten de method.

Hoe deze situatie hierboven is afgebeeld is niet juist. De referentie “vt2” is namelijk niet gelinkt naar de andere referentie. Een betere visualisering is waarschijnlijk:

Wat kunnen we concluderen van deze informatie:

Het Ref keyword wordt alleen gebruikt als je in een methode de meegegeven code referentie wil linken aan een ander object en je deze verandering buiten de methode nog wilt terugzien.


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development, .Net

Tags:


Reactie

  • Maarten Houtman schreef:

    Ik ben het niet helemaal eens met het onderscheid die je maakt tussen code reference en memory reference, deze zijn namelijk hetzelfde. Een referentie in je code naar een object is niets anders dan een pointer (uit bijvoorbeeld C) die kan wijzen naar het adres waar het daadwerkelijke object is te vinden. Wanneer de pointer niet geïnitialiseerd is, wijst deze standaard naar het geheugenadres 0. Daar komt dus de naam NullPointerException vandaan.

    Wanneer een methode wordt aangeroepen met als parameter een object, wordt het wat lastiger: Het object zelf wordt niet meegegeven, maar de pointer van het object wordt gekopieerd en meegegeven aan de methode. Valuetypes hebben standaard geen pointer die zal worden meegegeven aan de methode; in plaats van de pointer die wordt gekopieerd, wordt de valuetype zelf gekopieerd.

    Met ref geef je aan dat je graag een pointer wilt ontvangen in plaats van de valuetype. De taal/compiler/CLR zorgt er vervolgens voor dat jij als programmeur geen verschil ziet tussen ‘pass by value’ en ‘pass by reference’.

    Geplaatst op 11 december 2008 om 11:37 Permalink