Visual Basic 2012 Voorbeelden
   

visual basic 2012 broncode voorbeelden

Blijf op de hoogte van de recente aanpassingen op vbvoorbeelden!

Microsoft Visual Studio 2012Microsoft Developers Network - Visual BasicMicrosoft .NET Framework

13.5. Value versus Reference Types

Print Email Deel op Twitter Deel op Facebook

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.

13.5.1. Value Types

Elk type dat in de .NET runtime bruikbaar is moet een type zijn dat afgeleidt is van het type System.Object.
Een valuetype is een type dat overerft van System.ValueType, wat opzich weer een afgeleide is van System.Object.

De runtime gaat anders om met instanties van value en referencetypes.
Valuetype instanties worden op de "stack" ge-alloceerd, referencetype instanties worden op de "heap" ge-alloceerd.
Het alloceren of ophalen van data uit de stack gaat iets vlugger, alhouwel dit sterk afhankelijk is van het aantal instanties en het gebruikte platform.  Dit opzich zou men als een voordeel van valuetype kunnen beschouwen.

Als een dataholder gedeclareerd van een valuetype uit scope valt, dan wordt ook de valuetype instantie ge-associeerd met deze variabele uit het geheugen gehaald.
Dit is anders bij referencetypes.  Gezien meerdere variabelen kunnen wijzen naar een referencetype instantie, zal pas indien er geen verwijzingen meer bestaat naar die instantie, deze worden verwijderd (op het moment dat "garbage collection" optreedt).

Om een instantie van een referencetype te bewaren is vier extra bytes (32-bit platformen) nodig om de referentie/verwijzing naar het object te bewaren.

Memory management op de heap veroorzaakt dus een bepaalde overhead.  Deze overhead zou men kunnen vermijden door te werken met valuetype instanties.
Visual Basic 2012 Broncode - Codevoorbeeld 364
Option Strict On
Option Explicit On
Structure Coordinate
    Public Sub New(ByVal x As Integer, ByVal y As Integer)
        Me.X = x
        Me.Y = y
    End Sub
    Public Property X As Integer
    Public Property Y As Integer
    Public Overrides Function ToString() As String
        ToString = "(" & X.ToString() & "," & Y.ToString() & ")"
    End Function
End Structure
Class Example
    Public Shared Sub Main()
        Dim coordinate1 As Coordinate                                     ' (1)
        Console.WriteLine(coordinate1.ToString())
        '
        coordinate1.X = 1
        coordinate1.Y = 2
        Console.WriteLine(coordinate1.ToString())
        '
        Dim coordinate2 As Coordinate = New Coordinate()                  ' (2)
        Console.WriteLine(coordinate2.ToString())
        '
        Dim coordinate3 As Coordinate = New Coordinate(3, 4)              ' (3)
        Console.WriteLine(coordinate3.ToString())
        '
        coordinate3 = Nothing                                             ' (4)
        Console.WriteLine(coordinate3.ToString())
        '
        Dim coordinates(1000) As Coordinate                               ' (5)
        '
        Console.ReadLine()
    End Sub
End Class
Console Application Output
(0,0)
(1,2)
(0,0)
(3,4)
(0,0)
Wanneer een valuetype dataholder gedeclareerd wordt, is hier meteen een valuetype instantie aan gekoppeld (1).  Dit in tegenstelling tot een variabele gedeclareerd van een referencetype, deze bevat na declaratie Nothing en kan via een objectinitializer of via een andere objectexpressie een referentie worden toegekend.

In tegenstelling tot in een klasse, moet in een structure minimaal één instanceveld (of eventdefinition) opgenomen worden.  Velden uit een structure kunnen nooit geïnitialiseerd worden tijdens declaratie.
Een valuetype instantie moet immers steeds een gekende waarde bevatten.  De bedoeling hiervan is zo weinig mogelijk tijd te verliezen bij het aanmaken van instanties.  Zeker indien veel instanties van een valuetype nodig zijn, kan dit voordelig zijn.

Op regel (5) wordt een array geinitialiseerd van 1000 Coordinate elementen.
Net zoals arrays van eender welk ander valuetype element worden hier meteen 1000 instanties van die valuetype gecreëerd.
Hierbij zullen 1000 groepen van twee Integer instanties (voor _X en _Y) worden gereserveerd, wat erg snel kan gebeuren.  Op het moment dat onze arrayvariabele coordinates uit scope valt (op het eind van de Main procedure) kan meteen dit geheugen worden vrijgegeven.

13.5.2. Constructoren

Het is mogelijk een valuetype instantie, net als een referencetype instantie expliciet aan te maken via het New keyword, bijvoorbeeld : New Coordinate (2) of New Byte.  Het effect is hetzelfde indien men de expliciete constructor oproep weglaat, alle velden van deze valuetype instantie worden op hun default waarde gezet (of teruggezet indien de instantie reeds bestond).  Regel (1) en (2) leveren een identiek resultaat.

Doordat een valuetype instantie initieel een gekende waarde moet bevatten, kunnen er snel valuetype instanties worden aangemaakt.  Het is dan ook niet toegestaan een parameterloze constructor te definiëren.  Indien men dit toch mogelijk had gemaakt, zou er bij het creeren van de coordinates array  bijvoorbeeld, 1000 keer die constructor met initialisatiecode worden uitgevoerd, wat opzich het voordeel van dergelijke value types zou ondermijnen.
Enkel parameterloze constructoren die Shared zijn of niet parameterloze constructoren mogen worden toegevoegd.  Indien men de niet parameterloze constructor wil gebruiken, dient men deze expliciet aan te roepen (met het New keyword (3)).

13.5.3. Nothing

Ook indien men de valuetype variabele op Nothing instelt, bijvoorbeeld coordinate3 = Nothing (4) of aByte = Nothing, zullen alle velden van deze valuetype instantie op hun default waarde worden gezet.
Dit in tegenstelling tot het op Nothing instellen van een referencetype variabele, deze variabele wijst in dergelijk geval niet meer naar een object.
Een NullReferenceException treedt op indien je een instancemember aanroept op een objectexpressie die resulteert in Nothing.

13.5.4. Inheritance en Interface Implementatie

Structures kunnen net als klassen interfaces implementeren, inheritance is daarintegen onmogelijk.

13.5.5. Access Modifiers

Bijgevolg zijn ook de Protected en Protected Friend access modifiers voor members in structures niet bruikbaar.

De default access modifier (zoals bij gebruik van Dim) voor velden is steeds Public, bij klassen en modules is dit Private.

13.5.6. Kiezen tussen Value en Reference Types

De mogelijkheid user-defined valuetypes te creëren is voorzien om toe te laten datatypes te maken die net zo benaderbaar/behandelbaar zijn als "native datatypes" (zoals de eenvoudige intrensic datatypes als Byte, Integer, Char, ...).
De mogelijkheid bestaat van user-defined valuetypes variabele te declareren, zonder dat deze expliciet geïnstantieerd moet worden, met het eenvoudig principe dat een variabele rechtstreeks ge-associeerd is met een instantie, zonder dat hiermee de overhead gepaard gaat van bijvoorbeeld garbage collection.  Zie het topic over garbage collection voor meer details.

Zowel value als refencetypes zijn containertypes.
Een containertype is bruikbaar om een reeks van data - die nauw verbonden is met elkaar, maar niet in één elementaire type kan gestockeerd worden - te verzamelen.
Een klasse of een structure kan datamembers bevatten om deze gerelateerde data te bewaren en codemembers bevatten om deze data te manipuleren.
Naar die members kan individueel gerefereerd worden, of de container kan als een geheel benaderd worden.

Op het eerste zicht lijkt het alsof je met structures ruwweg hetzelfde kan gaan doen als met klassen.  Je kan je bijgevolg als ontwikkelaar gaan afvragen waarvoor je moet kiezen.  Een aantal significante verschillen worden hieronder nogmaals opgesomd :

- Storage Consumption :
Vier bytes (32-bit platformen) extra zijn nodig voor het verwijzen naar een referencetype instantie.  Enkel wanneer je applicatie een heel groot aantal instanties van dergelijk containertype nodig heeft, is dit punt van verschil significant.

- Onafhankelijke Instanties :
Twee of meer referencetype variabelen kunnen naar dezelfde referencetype instantie wijzen, dit is met valuetype variabelen onmogelijk.

- Perfomantie :
Referencetype instanties worden beheerd op de heap, valuetype instanties op de stack.  Er is een performance-overhead voor allocatie op de heap, het benaderen van die objecten, garbage collection op die objecten, ... .

- (Un)boxing :
Een valuetype wordt als object behandeld wanneer je deze toekent aan een Object variabele.  In dat geval zal de CLR deze waarde boxen en vaak ook moeten unboxen, de box wordt op de heap gemanaged.
Dergelijke behandeling van valuetype instanties is minder performant dan wanneer je meteen hiervoor een referencetype had gebruikt.

- Data Volume en Kopies van variabelen :
Bij een kopie van een referencetype variabele wordt slechts de vier-bytes ( 32-bit platformen) referentie gekopieerd, bij een kopie van een valuetype variabele wordt de volledige instantie gekopieerd.
Doorgaans wordt aangeraden het volume van valuetype instanties te beperken tot maximaal 16 bytes.

- Event Handling :
Bij structures kan enkel at runtime een shared eventhandler gekoppeld worden aan een event van die valuetype instance.  Terwijl men bij klassen over de volledige eventmogelijkheden beschikt, zowel koppeling at compile als at runtime.

Er zijn zoveel factoren die de keuze voor klassen of structures beïnvloeden dat het moeilijk is een algemene richtlijn te maken.  De uiteindelijke beslissing hangt af van de karakteristieken van je applicatie.

Soms kan je het containertype zowel met een structure of een class definiëren, in dergelijk geval kan je gaan experimenteren en de performance gaan vergelijken.

Toch nog een poging om enkel richtlijnen op te sommen.

Gebruik een valuetype wanneer :
- je een container voor een beperkte hoeveelheid data wil creëren
- je veel operaties op iedere instantie wil uitvoeren (en je dus geen last wil hebben van de performance-overhead van de heap)
- je zeker bent dat je al teveel (un)boxing kunt vermijden

Gebruik een referencetype wanneer :
- inheritance (polymorfimse) nodig is
- verschillende verwijzingen naar een instantie nodig hebt (graph-structuren, ...)
- de volledige eventmogelijkheden nodig zijn, bijvoorbeeld koppeling at compiletime
- men bij het instantiëren één of meerdere datamembers wil initialiseren

Indien je type niet aan bovenstaande kenmerken voldoet, gebruik dan een klasse.
Klassen zijn flexibelere dan structures en de storage en performance-overhead zijn meestal verwaarloosbaar.
Structures zijn eerder bedoeld voor types die zich als built-in types gedragen, en niet zozeer voor algemeen gebruik.

Dit artikel is gepubliceerd op maandag 15 oktober 2012 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.