Klassetypes zoals Circle (zie hoofdstuk klassen) worden anders behandeld.
Wanneer je een variabele van het type Circle declareert gaat de compiler geen geheugenblok reserveren dat groot genoeg is om een volledig Circle-object te bevatten. Integendeel, al wat gebeurt is dat de compiler een kliein stukje geheugen reserveert dat groot genoeg is om een geheugenadres te bevatten dat verwijst naar de plaats waar een Circle-object kan geplaatst worden.
Circle cirkel;
Het geheugen voor een volwaardig Circle-object wordt pas gereserveerd wanneer het sleutelwoord new wordt gebruikt.
Een klasse is een voorbeeld van een reference type: referentietypes houden referenties bij naar blokken geheugen.
De verschillen tussen value en reference types zijn van heel groot belang bij het programmeren.
int i = 42;
int copyi = i;
i++;
Debug.Print("i={0} , copyi={1}", i, copyi);
Hier zenden we de uitvoer die we vanuit Visual Studio kunnen zien via View > Output
Debug maakt deel uit van de namespace System.Diagnostics
i=43 , copyi=42
Merk op dat i werd opgehoogd tot 43, de waarde van copyi bleef 42.
Dit betekent dat bij de toekenning copyi = i de waarde van i in de geheugenplaats van copyi werd gekopiëerd, verder is er geen blijvende 'link' tussen i en copyi.
Circle c = new Circle(5);
Circle c2 = c;
c.radius++;
Debug.Print("Radius van c:{0} , Radius van c2:{1}"
, c.radius
, c2.radius);
Radius van c:6 , Radius van c2:6
Circle c
Circle c = new Circle(5);
Circle c2 = c;
c.radius++;
Circle c;
Op deze manier kan je een objectverwijzing tenietdoen door een variabele op null in te stellen:
Circle c = new Circle(7);
// statements
c = null; //c verwijst niet meer naar het object.
Circle c;
// statements
if(c==null)
{
MessageBox.Show("Het cirkel-object werd nog niet aangemaakt!");
}
Daar null eigenlijk een referentiewaarde is kan je null niet toewijzen aan value types:
int getal = null; //ongeldig!
In C# is het mogelijk om voor een value type variable aan te geven dat het nullable is:
int? getal = null; //geldig
Je kan voor een nullable type checken op een waarde null of gebruik maken van de eigenschap HasValue:
int? getal = null; if (getal.HasValue) { Debug.Print(getal.Value.ToString()); // of gewoon getal.ToString() } else { Debug.Print("getal = null"); }
De property Value is read-only, om een nieuwe waarde aan een nullable type toe te kennen gebruik je een gewoon toekenningsstatement.
int testgetal = 5;
Verhoog(testgetal);
Debug.Write("testGetal heeft de waarde: " + testgetal);
private int Verhoog(int i)
{
return i++;
}
testGetal heeft de waarde: 5
De methode Verhoog ontvangt de waarde 5, stopt deze in de variabele i en verhoogt de waarde van i.
Enkel de waarde van testgetal wordt gekopiëerd in i, er bestaat geen ander 'verband' of blijvende connectie tussen testgetal en i.
De waarde van de originele varibale testgetal blijft onveranderd!
Kan je binnen een methode dan geen value-type variabele zoals een int aanpassen? Dit kan wel, wanneer we een referentie naar deze variabele meegeven naar de methode, en in de methode de variabelereferentie ontvangen:
int testgetal = 5;
Verhoog(ref testgetal);
Debug.Write("testGetal heeft de waarde: " + testgetal);
private int Verhoog(ref int i)
{
return i++;
}
testGetal heeft de waarde: 6
int testgetal;
InitialiseerMe(out testgetal);
Debug.Write("testGetal heeft de waarde: " + testgetal);
private void InitialiseerMe(out int i)
{
i = 100;
}
Besturingssystemen en 'runtimes zoals de CLR - Common Language Runtime - voor .Net' gaan het beschikbare geheugen vaak opdelen in twee stukken die op een aparte manier benaderd worden: de stack en de heap.
| geheugen | omschrijving |
|---|---|
| stack | Wanneer je een mehode aanroept wordt het geheugen dat nodig is voor de parameters en lokale variabelen gebruikt van de stack. Wanneer de methode eindigt, wordt dit geheugen weer vrijgegeven. |
| heap | Wanneer je een oject aanmmaakt (instantie van een klasse) met het sleutelwoord new, dan wordt het geheugen dat nodig is om het object te bewaren gereserveerd op de heap. Naar zo'n object kunnen nu talloze referenties gelegd worden binnen een programma. Wanneer de laatste referentie naar een object verdwijnt, dan wordt dit geheugen weer vrijgegeven. De referentie (het adres waarnaar je verwijst) echter wel bewaard op de stack. |
De woorden stack en heap komen van de manier waarop het geheugen georganiseerd is:
Voorlopig is het voldoende in te zien dat System.Object de basisklasse is van alle klassen in .Net, als het ware de stamouder van alle klassen die we kennen of zelf maken.
Het sleutelwoord object is eigenlijk een alias voor System.Object, ze betekenen net hetzelfde.
int m = 42;
object o = m;
m++;
Debug.Print("m = " + m.ToString() + " / o=" + o.ToString());
m = 43 / o=42
Merk op dat m nog steeds naar de waarde op de stack verwijst.
int m = 42; object o = m;
int m = 42;
object o = m;
int k = o;
Om de waarde van o, die zich op de heap bevindt te kopiëren in de value type int variabele k moeten we o onderwerpen aan een typecast, we moeten tussen ronde haken expliciet aangeven naar welk type we het object gaan omzetten, hier naar het value type int:
int m = 42;
object o = m;
int k = (int)o;
Het proces waarbij een waarde op de heap gekopiëerd wordt naar de stack, voor gebruik in een value type variabele wordt unboxing genoemd: we halen de waarde uit zijn omkaderende objectdoos en plaatsen die op de stack.
We moeten ons hoeden voor InvalidCastException wanneer we aan typecasting doen:
Circle cirkel = new Circle(45);
object obj = cirkel;
int geheel = (int)obj;
Hier instantiëren we een Circle genaamd cirkel. We plaatsen de cirkel in een variabele van het type object genaamd obj.
In het derde statement gaan we een typecasting doen van het object naar het value-type int. Compile-time zien we geen fout, er wordt echter wel een fout gedetecteerd run-time: obj is een variabele van het type object die op de stack verwijst naar een Circle, dit kan daar alles in .Net, dus ook onze cirkel eigenlijk afstamt van object (de gegevens van cirkel bevindt zicht op de heap).
In het derde statement wordt gepoogd obj om te zetten in een int. Daar obj verwijst naar een object van het type Circle en niet naar een int-waarde kan deze omzetting niet gebeuren, een cirkel kan nu eenmaal niet zomaar omgezet worden in een geheel getal!
Circle cirkel = new Circle(45); object obj = cirkel; if (obj is int) { int geheel = (int)obj; Debug.Print("Ik ben een int met waarde = " + geheel.ToString()); } else if (obj is Circle) { Circle ronde = (Circle)obj; Debug.Print("Ik ben een cirkel met straal = " + ronde.radius); }
Ik ben een cirkel met straal = 45
Circle cirkel = new Circle(45); object obj = cirkel; int? geheel = obj as int?; if(geheel != null) { Debug.Print("Ik ben een int met waarde = " + geheel.ToString()); } Circle ronde = obj as Circle; if (ronde != null) { Debug.Print("Ik ben een cirkel met straal = " + ronde.radius); }
Met as doe je een typecasting, wanneer dit niet lukt heeft de variabele de waarde null.
Voorwaarde hierbij is dat het datatype waarnaar je converteert nullable is.
| Meer tutorials: |
| leer ook: | html | | xhtml | | css | | asp | | asp.net | | c# | | ado.net | | linq | | ajax | | java | | javascript |