c# : value en reference

  1. Voorbereiding
  2. Value types en objecten kopiëren
  3. De waarde null en Nullable types
  4. Parameters doorgeven 'by reference'
  5. out parameters opvullen
  6. Organisatie van het computergeheugen
  7. De klasse System.Object
  8. boxing
  9. unboxing
  10. Veilig casten met is en as

Voorbereiding

  • Maak een nieuwe Visual Studio solution Value_Reference.
  • Maak in de solution Value_Reference een nieuwe WPF Application WpfValue_Reference.
  • Voorzie een referentie naar Ivo.Hbo.Geometry (zie hoofdstuk klasssen)

  • voorzie in Window1.xaml.cs een using-statement:
  • Value types en objecten kopiëren

    types zoals int, float, double en char noemen we value types. Wanneer je een dergelijke variabele declareert genereert de compiler code die een stukje geheugen reserveert dat groot genoeg is om de overeenstemmede waarde te bevatten.
    Het declareren van een int variabele zorgt ervoor dat 4 bytes of 32 bits aan geheugen worden gereserveerd.
    Een statement dat ervoor zorgt dat bijvoorbeeld de waarde 42 aan de variabele wordt toegekend, zorgt ervoor dat de waarde 42 op de geheugenplaats wordt weggeschreven.

    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;
    
    Deze opdracht leidt dus enkel tot het alloceren van een klein stukje geheugen, groot genoeg om een ander geheugenadres te bevatten.

    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.

    De waarde null en Nullable types

    null

    Wanneer een reference-type variabele zoals een instantie van de klasse Circle (nog) niet verwijst naar een geheugenplaats:
    Circle c;
    
    c werd nog niet geïnitialiseerd.
    c is nu nog leeg, in C# betekent dit dat c de waarde null heeft.

    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.
    

    Nullable types

    De null value is handig om de checken of een objectvariabele geïnitialiseerd is.
    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.

    Parameters doorgeven 'by reference'

    Wanneer je een int, float, ... value type doorgeeft aan een mehode wordt de waarde van de variabele gekopiëerd binnen de methode:
    int testgetal = 5;
    Verhoog(testgetal);
    Debug.Write("testGetal heeft de waarde: " + testgetal);
    
    De methode Verhoog:
    private int Verhoog(int i)
    {
       return i++;
    }
    
    De uitvoer:
    testGetal heeft de waarde: 5
    
    Wellicht ben je enigzins verrast door dit resultaat, misschien had je hier 6 verwacht?

    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);
    
    De methode Verhoog:
    private int Verhoog(ref int i)
    {
       return i++;
    }
    
    De uitvoer:
    testGetal heeft de waarde: 6
    

    out parameters opvullen

    Een methode kan een variabele ook initialiseren:
    int testgetal;
    
    InitialiseerMe(out testgetal);
    Debug.Write("testGetal heeft de waarde: " + testgetal);
    
    private void InitialiseerMe(out int i)
    {
    	i = 100;
    }
    

    Organisatie van het computergeheugen

    Computers gebruiken geheugen om programma's uit te voeren en de gerbuikte data vast te houden.
    Om het verschil in te zien tussen value types en reference types is het handig in te zien hoe het computergeheugen georganiseerd is.

    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:

    De klasse System.Object

    Een van de belangrijkste referentietypes uit het volledige .Net Framework is de klasse Object uit de namespace System.
    Om dit volledig in te zien is het nodig overerving te begrijpen, wat later in de cursus aan bod komt.

    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.

    Boxing

    int m = 42;
    object o = m;
    m++;
    Debug.Print("m = " + m.ToString() + " / o=" + o.ToString());
    
    Geeft als uitvoer:
    m = 43 / o=42
    

    Unboxing

    Beschouw het vorige voorbeeld:
    int m = 42;
    object o = m;
    
    Wanneer je de waarde van o nu in een nieuwe int variabele k wil stoppen, dan verwacht je misschien dat dit zal lukken:
    int m = 42;
    object o = m;
    int k = o;
    
    Dit leidt echter tot een compileerfout:
    Cannot implicitly convert type 'object' to 'int'. An explicit conversion exists (are you missing a cast?)

    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!

    Veilig casten met is en as

    Om als programmeur niet steeds met de daver op het hart te zitten dat je runtime fouten zal krijgen door een verkeerde typecast zijn er mogelijkheden om op een veilige manier aan typecasting te doen.

    is

    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);
    }
    
    Output:
    Ik ben een cirkel met straal = 45
    

    as

    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
    Valid HTML 4.01! Valid CSS! © - Cursusweb