Skip to content

3. Klassen, structuren, interfaces

3.1. Het object aan de hand van het voorbeeld

3.1.1. Algemeen

We gaan nu aan de hand van een voorbeeld in op objectgeoriënteerd programmeren. Een object is een entiteit die gegevens bevat die de toestand ervan bepalen (dit noemen we eigenschappen) en functies (dit noemen we methoden). Een object wordt aangemaakt volgens een model dat we een klasse noemen:

Public Class c1
     ' attributen
    Private p1 As type1
    Private p2 As type2
....

     ' methode
    Public Sub m1(....)
...
    End Sub    

     ' methode
    Public Function m2(...)
....
    End Function    
End Class

Op basis van de vorige klasse C1 kunnen we talrijke objecten aanmaken, zoals O1, O2, … Ze zullen allemaal de eigenschappen p1, p2, … en de methoden m3, m4, … hebben. Maar ze zullen verschillende waarden hebben voor hun eigenschappen pi, waardoor ze elk een eigen toestand hebben. Naar analogie geldt de declaratie

    dim i, j as integer

twee objecten (de term is hier onjuist) van het type (klasse) Integer. Hun enige eigenschap is hun waarde. Als O1 een object is van het type C1, verwijst O1.p1 naar de eigenschap p1 van O1 en O1.m1 naar de methode m1 van O1. Laten we een eerste objectmodel bekijken: de klasse personne.

3.1.2. Definitie van de klasse ‘persoon’

De definitie van de klasse personne luidt als volgt:


Public Class personne
    ' attributen
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' methode
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    ' methode
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

Dit is de definitie van een klasse, dus van een gegevenstype. Wanneer we variabelen van dit type aanmaken, noemen we ze objecten of instanties van klassen. Een klasse is dus een mal op basis waarvan objecten worden geconstrueerd. De leden of velden van een klasse kunnen gegevens (attributen), methoden (functies) of eigenschappen zijn. Eigenschappen zijn speciale methoden die dienen om de waarde van attributen van het object te achterhalen of vast te leggen. Deze velden kunnen vergezeld gaan van een van de volgende drie sleutelwoorden:

privé
Een privéveld (private) is alleen toegankelijk via de interne methoden van de klasse
public
Een openbaar (public) veld is toegankelijk voor elke functie, ongeacht of deze al dan niet binnen de klasse is gedefinieerd
protégé
Een beschermd veld (protected) is alleen toegankelijk via de interne methoden van de klasse of van een afgeleid object (zie later het concept van overerving).

Over het algemeen worden de gegevens van een klasse als privé gedeclareerd, terwijl de methoden en eigenschappen als openbaar worden gedeclareerd. Dit betekent dat de gebruiker van een object (de programmeur):

  • geen directe toegang heeft tot de privégegevens van het object
  • gebruik kan maken van de openbare methoden van het object, en met name van die methoden die toegang geven tot de privégegevens ervan.

De syntaxis voor het declareren van een klasse is als volgt:


public class classe
    private  donnée ou méthode ou propriété privée
    public  donnée ou méthode ou propriété publique
    protected  donnée ou méthode ou propriété protégée
end class

De volgorde waarin de attributen *private, protected en public* worden gedeclareerd, maakt niet uit.

3.1.3. De methode initialiseert

Laten we teruggaan naar onze klasse [personne], die als volgt is gedeclareerd:


Public Class personne
    ' attributen
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' methode
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    ' methode
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

Wat is de functie van de methode initialise? Omdat nom, prenom en age privégegevens zijn van de klasse personne, leiden de instructies:

dim p1 as personne p1
p1.prenom="Jean"
p1.nom="Dupont"
p1.age=30

zijn ongeldig. We moeten een object van het type personne initialiseren via een openbare methode. Dat is de taak van de methode initialise. We schrijven dan:

dim p1 as personne
p1.initialise("Jean","Dupont",30)

De notatie p1.initialise is toegestaan, omdat initialise openbaar toegankelijk is.

3.1.4. De operator new

De reeks instructies

dim p1 as personne
p1.initialise("Jean","Dupont",30)

is onjuist. De instructie

dim p1 as personne

definieert p1 als een verwijzing naar een object van het type personne. Dit object bestaat nog niet en daarom is p1 niet geïnitialiseerd. Het is alsof men het volgende zou schrijven:

dim p1 as personne=nothing

waarbij we met het sleutelwoord nothing expliciet aangeven dat de variabele p1 nog niet naar een object verwijst. Wanneer we vervolgens schrijven

p1.initialise("Jean","Dupont",30)

roep je de methode initialise aan van het object waarnaar p1 verwijst. Dit object bestaat echter nog niet en de compiler zal de fout melden. Om ervoor te zorgen dat p1 naar een object verwijst, moet je het volgende schrijven:

dim p1 as personne=new personne()

Hierdoor wordt een nog niet geïnitialiseerd object van het type personne aangemaakt: de attributen nom en prenom, die verwijzingen zijn naar objecten van het type String, krijgen de waarde nothing, en age de waarde 0. Er vindt dus een standaardinitialisatie plaats. Nu p1 naar een object verwijst, is de initialisatie-instructie voor dit object

p1.initialise("Jean","Dupont",30)

geldig.

3.1.5. Het sleutelwoord Me

Laten we de code van de methode initialise eens bekijken:


    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub    

De instructie Me.prenom=P betekent dat het attribuut prenom van het huidige object (Me) de waarde P krijgt. Het sleutelwoord Me verwijst naar het huidige object: het object waarin de uitgevoerde methode zich bevindt. Hoe weten we dat? Laten we eens kijken hoe het object waarnaar p1 verwijst, wordt geïnitialiseerd in het aanroepende programma:

p1.initialise("Jean","Dupont",30)

Er wordt een beroep gedaan op de methode initialise van het object p1. Wanneer in deze methode naar het object Me wordt verwezen, wordt in feite naar het object p1 verwezen. De methode initialise had ook als volgt geschreven kunnen worden:


    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        prenom = P
        nom = N
        Me.age = age
    End Sub    

Wanneer een methode van een object verwijst naar een attribuut A van dat object, wordt de notatie Me.A impliciet gebruikt. Deze moet expliciet worden gebruikt wanneer er een conflict is tussen identificatoren. Dit is het geval bij de instructie:


Me.age=age;

waarbij age verwijst naar zowel een attribuut van het huidige object als naar de parameter age die door de methode wordt ontvangen. De dubbelzinnigheid moet dan worden opgeheven door het attribuut age aan te duiden als Me.age.

3.1.6. Een testprogramma

Hier volgt een kort testprogramma:


Public Class personne
    ' attributen
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' methode
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    ' methode
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

en de verkregen resultaten:

dos>vbc personne1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>personne1
Jean,Dupont,30

3.1.7. Een bestand met gecompileerde klassen (assembly) gebruiken

Merk op dat er in het vorige voorbeeld twee klassen in ons testprogramma voorkomen: de klassen personne en test1. Er is nog een andere manier om dit aan te pakken:

  • we compileren de klasse ‘persoon’ in een apart bestand dat een assembly wordt genoemd. Dit bestand heeft de extensie .dll

  • we compileren de klasse test1 door te verwijzen naar de assembly die de klasse personne bevat.

De twee bronbestanden zien er dan als volgt uit:

test.vb

Module test1
    Sub Main()
        Dim p1 As New personne
        p1.initialise("Jean", "Dupont", 30)
        p1.identifie()
    End Sub
End module
personne2.vb

' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

Public Class personne
    ' attributen
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' methode
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub    'initialise

    ' methode
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub    'identifie
End Class 'personne

De klasse personne wordt gecompileerd met de volgende instructie:

dos>vbc /t:library personne2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>dir
24/02/2004  16:50                  509 personne2.vb
24/02/2004  16:49                  143 test.vb
24/02/2004  16:50                3 584 personne2.dll

De compilatie heeft een bestand personne2.dll opgeleverd. Het is de compilatieoptie /t:library die aangeeft dat er een "assembly"-bestand moet worden gegenereerd. Laten we nu het bestand test.vb compileren:

dos>vbc /r:personne2.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
24/02/2004  16:50                  509 personne2.vb
24/02/2004  16:49                  143 test.vb
24/02/2004  16:50                3 584 personne2.dll
24/02/2004  16:51                3 072 test.exe

De compilatieoptie /r:personne2.dll geeft de compiler aan dat hij bepaalde klassen zal vinden in het bestand personne2.dll. Wanneer de compiler in het bronbestand test.vb een verwijzing aantreft naar de klasse personne – een klasse die niet is gedeclareerd in het bronbestand test.vb – zal hij zoeken naar de klasse personne in de .dll-bestanden waarnaar wordt verwezen door de optie /r. Hier vindt hij de klasse personne in de assembly personne2.dll. Er hadden ook andere klassen in deze assembly kunnen worden opgenomen. Om bij het compileren meerdere bestanden met gecompileerde klassen te gebruiken, schrijft men:

vbc /r:fic1.dll /r:fic2.dll ... fichierSource.vb

Het uitvoeren van het programma test1.exe levert de volgende resultaten op:

dos>test
Jean,Dupont,30

3.1.8. Een andere methode initialiseert

Laten we nogmaals de klasse personne bekijken en de volgende methode eraan toevoegen:


    ' methode
    Public Sub initialise(ByVal P As personne)
        prenom = P.prenom
        nom = P.nom
        Me.age = P.age
    End Sub

We hebben nu twee methoden met de naam initialise: dit is toegestaan zolang ze verschillende parameters accepteren. Dat is hier het geval. De parameter is nu een verwijzing P naar een persoon. De attributen van de persoon P worden vervolgens toegewezen aan het huidige object (Me). Merk op dat de methode initialise directe toegang heeft tot de attributen van het object P, hoewel deze van het type private zijn. Dit geldt altijd: een object O1 van de klasse C heeft altijd toegang tot de attributen van objecten van dezelfde klasse C. Hier volgt een test van de nieuwe klasse personne, die zoals eerder uitgelegd is gecompileerd in personne.dll:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

Module test1
    Sub Main()
        Dim p1 As New personne
        p1.initialise("Jean", "Dupont", 30)
        Console.Out.Write("p1=")
        p1.identifie()
        Dim p2 As New personne
        p2.initialise(p1)
        Console.Out.Write("p2=")
        p2.identifie()
    End Sub
End Module

en de resultaten daarvan:

p1=Jean,Dupont,30
p2=Jean,Dupont,30

3.1.9. Constructors van de klasse ‘persoon’

Een constructor is een procedure met de naam New die wordt aangeroepen bij het aanmaken van het object. Deze wordt doorgaans gebruikt om het object te initialiseren. Als een klasse een constructor heeft die n argumenten accepteert argi, kunnen de declaratie en initialisatie van een object van deze klasse als volgt plaatsvinden:


        dim objet as classe =new classe(arg1,arg2, ... argn)

of


        dim objet as classe

        objet=new classe(arg1,arg2, ... argn)

Wanneer een klasse één of meerdere constructors heeft, moet er verplicht één van deze constructors worden gebruikt om een object van deze klasse aan te maken. Als een klasse C geen constructors heeft, beschikt deze over een standaardconstructor, namelijk de constructor zonder parameters: public New(). De attributen van het object worden dan geïnitialiseerd met standaardwaarden. Dit is wat er gebeurde in de voorgaande programma's, waar we het volgende hadden geschreven:

    dim p1 as personne
    p1=new personne

Laten we twee constructors maken voor onze klasse personne:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' de klasse 'persoon'
Public Class personne
    ' attributen
    Private prenom As String
    Private nom As String
    Private age As Integer

    ' constructors
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        initialise(P, N, age)
    End Sub

    Public Sub New(ByVal P As personne)
        initialise(P)
    End Sub

    ' methoden voor het initialiseren van het object
    Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    Public Sub initialise(ByVal P As personne)
        prenom = P.prenom
        nom = P.nom
        Me.age = P.age
    End Sub

    ' methode
    Public Sub identifie()
        Console.Out.WriteLine((prenom & "," & nom & "," & age))
    End Sub
End Class

Onze twee constructors roepen gewoon de bijbehorende methoden initialise aan. Ter herinnering: wanneer we in een constructor bijvoorbeeld de notatie initialise(P) tegenkomen, vertaalt de compiler dit naar Me.initialise(P). In de constructor wordt de methode initialise dus aangeroepen om te werken aan het object waarnaar wordt verwezen door Me, dat wil zeggen het huidige object, het object dat op dat moment wordt geconstrueerd. Hier volgt een kort testprogramma:


' opties 
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        Dim p1 As New personne("Jean", "Dupont", 30)
        Console.Out.Write("p1=")
        p1.identifie()
        Dim p2 As New personne(p1)
        Console.Out.Write("p2=")
        p2.identifie()
    End Sub
End Module

en de verkregen resultaten:

p1=Jean,Dupont,30
p2=Jean,Dupont,30

3.1.10. Objectreferenties

We gebruiken altijd dezelfde klasse personne. Het testprogramma ziet er als volgt uit:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        ' p1
        Dim p1 As New personne("Jean", "Dupont", 30)
        Console.Out.Write("p1=")
        p1.identifie()

        ' p2 verwijst naar hetzelfde object als p1
        Dim p2 As personne = p1
        Console.Out.Write("p2=")
        p2.identifie()

        ' p3 verwijst naar een object dat een kopie zal zijn van het object waarnaar p1 verwijst
        Dim p3 As New personne(p1)
        Console.Out.Write("p3=")
        p3.identifie()

        ' we wijzigen de status van het object waarnaar p1 verwijst
        p1.initialise("Micheline", "Benoît", 67)
        Console.Out.Write("p1=")
        p1.identifie()

        ' aangezien p2 = p1, moet de toestand van het object waarnaar p2 verwijst, zijn gewijzigd
        Console.Out.Write("p2=")
        p2.identifie()

        ' aangezien p3 niet naar hetzelfde object verwijst als p1, kan het object waarnaar p3 verwijst niet van status zijn veranderd
        Console.Out.Write("p3=")
        p3.identifie()
    End Sub
End Module

De verkregen resultaten zijn als volgt:

p1=Jean,Dupont,30
p2=Jean,Dupont,30
p3=Jean,Dupont,30
p1=Micheline,Benoît,67
p2=Micheline,Benoît,67
p3=Jean,Dupont,30

Wanneer de variabele p1 wordt gedeclareerd met

dim p1 as personne=new personne("Jean","Dupont",30)

verwijst p1 naar het object personne("Jean","Dupont",30), maar is niet het object zelf. In C zouden we zeggen dat het een pointer is, c.a.d, het adres van het aangemaakte object. Als we vervolgens schrijven:

    p1=nothing

Dan wordt niet het object personne("Jean","Dupont",30) gewijzigd, maar verandert de waarde van de verwijzing p1. Het object personne("Jean","Dupont",30) gaat „verloren” als er geen enkele andere variabele naar verwijst.

Wanneer men schrijft:

dim p2 as personne=p1

wordt de pointer p2 geïnitialiseerd: deze ‘wijst’ naar hetzelfde object (verwijst naar hetzelfde object) als de pointer p1. Als we dus het object wijzigen waarnaar p1 „wijst” (of waarnaar wordt verwezen), wijzigen we ook het object waarnaar p2 verwijst.

Wanneer we schrijven:

dim p3 as personne =new personne(p1);

wordt er een nieuw object aangemaakt, een kopie van het object waarnaar wordt verwezen door p1. Naar dit nieuwe object wordt verwezen door p3. Als het object waarnaar wordt verwezen door p1 wordt gewijzigd, heeft dit geen enkele invloed op het object waarnaar wordt verwezen door p3. Dit blijkt uit de verkregen resultaten.

3.1.11. Tijdelijke objecten

In een uitdrukking kan expliciet een beroep worden gedaan op de constructor van een object: dit object wordt aangemaakt, maar we hebben er geen toegang toe (om het bijvoorbeeld te wijzigen). Dit tijdelijke object wordt aangemaakt om de uitdrukking te kunnen evalueren en wordt daarna vrijgegeven. De geheugenruimte die het in beslag nam, wordt later automatisch vrijgemaakt door een programma dat ‘garbage collector’ wordt genoemd en dat tot taak heeft de geheugenruimte vrij te maken die wordt ingenomen door objecten waarnaar niet langer wordt verwezen door gegevens in het programma. Laten we het volgende nieuwe testprogramma eens bekijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        Dim p As New personne(New personne("Jean", "Dupont", 30))
        p.identifie()
    End Sub
End Module

en laten we de constructors van de klasse personne aanpassen zodat ze een bericht weergeven:


    ' constructors
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        Console.Out.WriteLine("Constructeur personne(String, String, integer)")
        initialise(P, N, age)
    End Sub

    Public Sub New(ByVal P As personne)
        Console.Out.WriteLine("Constructeur personne(personne)")
        initialise(P)
    End Sub

We krijgen de volgende resultaten:

dos>test
Constructeur personne(String, String, integer)
Constructeur personne(personne)
Jean,Dupont,30

waarin de opeenvolgende opbouw van de twee tijdelijke objecten wordt weergegeven.

3.1.12. Methoden voor het lezen en schrijven van privé-attributen

We voegen aan de klasse personne de methoden toe die nodig zijn om de status van de attributen van de objecten te lezen of te wijzigen:


Imports System

Public Class personne

    ' attributen
    Private prenom As [String]
    Private nom As [String]
    Private age As Integer

    ' constructors
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        Me.prenom = P
        Me.nom = N
        Me.age = age
    End Sub

    Public Sub New(ByVal P As personne)
        Me.prenom = P.prenom
        Me.nom = P.nom
        Me.age = P.age
    End Sub

    ' identificatoren
    Public Sub identifie()
        Console.Out.WriteLine((prenom + "," + nom + "," + age))
    End Sub

    ' accessors
    Public Function getPrenom() As [String]
        Return prenom
    End Function

    Public Function getNom() As [String]
        Return nom
    End Function

    Public Function getAge() As Integer
        Return age
    End Function

    ' modifiers
    Public Sub setPrenom(ByVal P As [String])
        Me.prenom = P
    End Sub

    Public Sub setNom(ByVal N As [String])
        Me.nom = N
    End Sub

    Public Sub setAge(ByVal age As Integer)
        Me.age = age
    End Sub
End Class

We testen de nieuwe klasse met het volgende programma:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' testpagina
Public Module test
    Sub Main()
        Dim P As New personne("Jean", "Michelin", 34)
        Console.Out.WriteLine(("P=(" & P.getPrenom() & "," & P.getNom() & "," & P.getAge() & ")"))
        P.setAge(56)
        Console.Out.WriteLine(("P=(" & P.getPrenom() & "," & P.getNom() & "," & P.getAge() & ")"))
    End Sub
End Module

en we krijgen de volgende resultaten:

P=(Jean,Michelin,34)
P=(Jean,Michelin,56)

3.1.13. De eigenschappen

Er is nog een andere manier om toegang te krijgen tot de attributen van een klasse, namelijk door eigenschappen aan te maken. Hiermee kunnen we privé-attributen bewerken alsof ze openbaar zijn. Laten we eens kijken naar de volgende klasse personne, waarin de eerdere accessors en modifiers zijn vervangen door eigenschappen die zowel lees- als schrijftoegang bieden:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' klasse persoon
Public Class personne

    ' attributen
    Private _prenom As [String]
    Private _nom As [String]
    Private _age As Integer


    ' fabrikanten
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        Me._prenom = P
        Me._nom = N
        Me._age = age
    End Sub


    Public Sub New(ByVal P As personne)
        Me._prenom = P._prenom
        Me._nom = P._nom
        Me._age = P._age
    End Sub

    ' identificeert
    Public Sub identifie()
        Console.Out.WriteLine((_prenom & "," & _nom & "," & _age))
    End Sub

    ' eigenschappen
    Public Property prenom() As String
        Get
            Return _prenom
        End Get
        Set(ByVal Value As String)
            _prenom = Value
        End Set
    End Property

    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = Value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            ' geldige leeftijd?
            If Value >= 0 Then
                _age = Value
            Else
                Throw New Exception("âge (" & Value & ") invalide")
            End If
        End Set
    End Property
End Class

Met een eigenschap (Property) kun je de waarde van een attribuut opvragen (get) of instellen (set). In ons voorbeeld hebben we de namen van de attributen voorafgegaan met het teken _, zodat de eigenschappen dezelfde naam dragen als de primitieve attributen. Een eigenschap mag namelijk niet dezelfde naam hebben als het attribuut dat ze beheert, omdat er dan een naamconflict in de klasse ontstaat. We hebben onze attributen daarom _prenom, _nom en _age genoemd en de constructors en methoden dienovereenkomstig aangepast. Vervolgens hebben we drie eigenschappen aangemaakt: nom, prenom en age. Een eigenschap wordt als volgt gedeclareerd:


    Public Property nom() As Type
        Get
...
        End Get
        Set(ByVal Value As Type)
...
        End Set    
    End Property

waarbij Type het type moet zijn van het attribuut dat door de eigenschap wordt beheerd. De eigenschap kan twee methoden hebben, namelijk get en set. De get-methode is doorgaans verantwoordelijk voor het retourneren van de waarde van het attribuut dat zij beheert (zij zou ook iets anders kunnen retourneren; niets staat dat in de weg). De methode set ontvangt een parameter genaamd **value**, die ze normaal gesproken toekent aan het attribuut dat ze beheert. Ze kan van de gelegenheid gebruikmaken om de geldigheid van de ontvangen waarde te controleren en eventueel een uitzondering te genereren als de waarde ongeldig blijkt te zijn. Dit is wat hier wordt gedaan voor de leeftijd.

Hoe worden deze get- en set-methoden aangeroepen? Laten we eens kijken naar het volgende testprogramma:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        Dim P As New personne("Jean", "Michelin", 34)
        Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))
        P.age = 56
        Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))
        Try
            P.age = -4
        Catch ex As Exception
            Console.Error.WriteLine(ex.Message)
        End Try
    End Sub
End Module

In de instructie

        Console.Out.WriteLine(("P=(" & P.prenom & "," & P.nom & "," & P.age & ")"))

wordt getracht de waarden van de eigenschappen prenom, nom en age van persoon P op te halen. De get-methode van deze eigenschappen wordt dan aangeroepen en retourneert de waarde van het attribuut dat ze beheren.

In de instructie

        P.age = 56

willen we de waarde van de eigenschap age vastleggen. De set-methode van deze eigenschap wordt dan aangeroepen. Deze ontvangt 56 als parameter value.

Een eigenschap P van een klasse C die alleen de get-methode definieert, wordt ‘alleen-lezen’ genoemd. Als c een object van klasse C is, wordt de bewerking c.P=waarde dan door de compiler geweigerd.

De uitvoering van het voorgaande testprogramma levert de volgende resultaten op:

P=(Jean,Michelin,34)
P=(Jean,Michelin,56)
âge (-4) invalide

Met eigenschappen kunnen we dus privé-attributen bewerken alsof ze openbaar zijn.

3.1.14. Methoden en klasse-attributen

Stel dat we het aantal [personne]-objecten willen tellen dat in een applicatie is aangemaakt. We kunnen zelf een teller bijhouden, maar dan lopen we het risico dat we tijdelijke objecten vergeten die hier en daar worden aangemaakt. Het lijkt veiliger om in de constructors van de klasse [personne] een instructie op te nemen die een teller verhoogt. Het probleem is dat er een verwijzing naar deze teller moet worden doorgegeven, zodat de constructor deze kan verhogen: er moet een nieuwe parameter aan worden doorgegeven. Men kan de teller ook opnemen in de definitie van de klasse. Aangezien het een attribuut is van de klasse zelf en niet van een specifiek object van deze klasse, wordt het anders gedeclareerd met het sleutelwoord Shared:

    Private Shared _nbPersonnes As Long = 0

Om ernaar te verwijzen, schrijven we personne._nbPersonnes om aan te geven dat het een attribuut is van de klasse personne zelf. Hier hebben we een privé-attribuut aangemaakt waartoe we buiten de klasse geen directe toegang hebben. We maken daarom een openbare eigenschap aan om toegang te geven tot het klasseattribuut nbPersonnes. Om de waarde van nbPersonnes in te stellen, heeft de methode get van deze eigenschap geen specifiek personne-object nodig: _nbPersonnes is namelijk niet het attribuut van een specifiek object, maar het attribuut van een hele klasse. Daarom is er ook een eigenschap nodig die eveneens is gedeclareerd als Shared:

    Public Shared ReadOnly Property nbPersonnes() As Long
        Get
            Return _nbPersonnes
        End Get
    End Property

die van buitenaf wordt aangeroepen met de syntaxis personne.nbPersonnes. De eigenschap is als alleen-lezen gedefinieerd (ReadOnly) omdat er geen set-methode voor is. Hier volgt een voorbeeld. De klasse personne ziet er als volgt uit:


Option Explicit On 
Option Strict On

' naamruimten
Imports System

' klasse
Public Class personne
    ' klasseattributen
    Private Shared _nbPersonnes As Long = 0

    ' instantieattributen
    Private _prenom As [String]
    Private _nom As [String]
    Private _age As Integer

    ' constructors
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        ' nog iemand
        _nbPersonnes += 1
        Me._prenom = P
        Me._nom = N
        Me._age = age
    End Sub

    Public Sub New(ByVal P As personne)
        ' nog een persoon
        _nbPersonnes += 1
        Me._prenom = P._prenom
        Me._nom = P._nom
        Me._age = P._age
    End Sub

    ' identificeert
    Public Sub identifie()
        Console.Out.WriteLine((_prenom & "," & _nom & "," & _age))
    End Sub

    ' klasse-eigenschap
    Public Shared ReadOnly Property nbPersonnes() As Long
        Get
            Return _nbPersonnes
        End Get
    End Property

    ' instantie-eigenschappen
    Public Property prenom() As String
        Get
            Return _prenom
        End Get
        Set(ByVal Value As String)
            _prenom = Value
        End Set
    End Property

    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = Value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            ' geldige leeftijd?
            If Value >= 0 Then
                _age = Value
            Else
                Throw New Exception("âge (" & Value & ") invalide")
            End If
        End Set
    End Property
End Class

Met het volgende programma:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' test-pg
Module test
    Sub Main()
        Dim p1 As New personne("Jean", "Dupont", 30)
        Dim p2 As New personne(p1)
        Console.Out.WriteLine(("Nombre de personnes créées : " & personne.nbPersonnes))
    End Sub
End Module

krijgt men de volgende resultaten:

    Nombre de personnes créées : 2

3.1.15. Overdracht van een object aan een functie

We hebben al gezegd dat VB.NET standaard de effectieve parameters van een functie per waarde doorgeeft: de waarden van de effectieve parameters worden gekopieerd naar de formele parameters. In het geval van een object moet men zich niet laten misleiden door het systematische taalgebruik waarbij men spreekt van een object in plaats van een objectreferentie. Een object wordt uitsluitend gemanipuleerd via een verwijzing (een pointer) ernaar. Wat dus aan een functie wordt doorgegeven, is niet het object zelf, maar een verwijzing naar dat object. Het is dus de waarde van de verwijzing en niet de waarde van het object zelf die in de formele parameter wordt gekopieerd: er wordt geen nieuw object aangemaakt. Als een objectreferentie R1 aan een functie wordt doorgegeven, wordt deze gekopieerd naar de bijbehorende formele parameter R2. De verwijzingen R2 en R1 verwijzen dus naar hetzelfde object. Als de functie het object wijzigt waarnaar R2 verwijst, wijzigt zij uiteraard ook het object waarnaar R1 verwijst, aangezien het om hetzelfde object gaat.

Dit wordt geïllustreerd in het volgende voorbeeld:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        ' een persoon p1
        Dim p1 As New personne("Jean", "Dupont", 30)

        ' weergave p1
        Console.Out.Write("Paramètre effectif avant modification : ")
        p1.identifie()

        ' wijziging p1
        modifie(p1)

        ' weergave p1
        Console.Out.Write("Paramètre effectif après modification : ")
        p1.identifie()
    End Sub

    Sub modifie(ByVal P As personne)
        ' weergave persoon P
        Console.Out.Write("Paramètre formel avant modification : ")
        P.identifie()
        ' wijziging P
        P.prenom = "Sylvie"
        P.nom = "Vartan"
        P.age = 52
        ' P weergeven
        Console.Out.Write("Paramètre formel après modification : ")
        P.identifie()
    End Sub
End Module

De verkregen resultaten zijn als volgt:

Paramètre effectif avant modification : Jean,Dupont,30
Paramètre formel avant modification : Jean,Dupont,30
Paramètre formel après modification : Sylvie,Vartan,52
Paramètre effectif après modification : Sylvie,Vartan,52

We zien dat er slechts één object is aangemaakt: dat van de persoon p1 uit de procedure Main, en dat het object inderdaad is gewijzigd door de functie modifie.

3.1.16. Een array van personen

Een object is een gegeven zoals elk ander en daarom kunnen meerdere objecten in een array worden verzameld:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        ' een lijst met personen
        Dim amis(2) As personne
        amis(0) = New personne("Jean", "Dupont", 30)
        amis(1) = New personne("Sylvie", "Vartan", 52)
        amis(2) = New personne("Neil", "Armstrong", 66)
        ' weergave
        Console.Out.WriteLine("----------------")
        Dim i As Integer
        For i = 0 To amis.Length - 1
            amis(i).identifie()
        Next i
    End Sub
End Module

De instructie Dim vrienden(2) As persoon creëert een array met 3 elementen van het type personne. Deze 3 elementen worden hier geïnitialiseerd met de waarden nothing en c.a.d, waarbij ze naar geen enkel object verwijzen. Ook hier wordt, in de volksmond, gesproken van een array van objecten, terwijl het slechts een array van verwijzingen naar objecten is. Het aanmaken van de array van objecten, die zelf een object is, creëert geen enkel object van het type van de elementen ervan: dat moet daarna gebeuren. We krijgen de volgende resultaten:

Jean,Dupont,30
Sylvie,Vartan,52
Neil,Armstrong,66

3.2. Overerving aan de hand van een voorbeeld

3.2.1. Algemeen

Hier bespreken we het begrip overerving. Het doel van overerving is om een bestaande klasse aan te passen zodat deze aan onze behoeften voldoet. Stel dat we een klasse enseignant willen maken: een leraar is een specifiek persoon. Hij heeft eigenschappen die een ander niet heeft: het vak dat hij onderwijst bijvoorbeeld. Maar hij heeft ook de eigenschappen die iedereen heeft: voornaam, achternaam en leeftijd. Een leraar maakt dus volledig deel uit van de klasse personne, maar heeft extra eigenschappen. In plaats van een klasse enseignant helemaal vanaf nul te schrijven, geven we er de voorkeur aan om voort te bouwen op de bestaande klasse personne en deze aan te passen aan het specifieke karakter van leraren. Het concept van overerving maakt dit mogelijk. Om aan te geven dat de klasse enseignant de eigenschappen van de klasse personne erft, schrijven we:


Public Class enseignant
    Inherits personne

Let op de specifieke syntaxis over twee regels. De klasse personne wordt de bovenliggende (of moeder)klasse genoemd en de klasse enseignant de afgeleide (of dochter)klasse. Een object enseignant bezit alle eigenschappen van een object personne: het heeft dezelfde attributen en dezelfde methoden. Deze attributen en methoden van de bovenliggende klasse worden niet herhaald in de definitie van de afgeleide klasse: er worden alleen de attributen en methoden vermeld die door de afgeleide klasse zijn toegevoegd. We nemen aan dat de klasse personne als volgt is gedefinieerd:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' klasse persoon
Public Class personne

    ' klasseattributen
    Private Shared _nbPersonnes As Long = 0

    ' instantieattributen
    Private _prenom As [String]
    Private _nom As [String]
    Private _age As Integer


    ' constructors
    Public Sub New(ByVal P As [String], ByVal N As [String], ByVal age As Integer)
        ' nog een persoon
        _nbPersonnes += 1
        ' constructie
        Me._prenom = P
        Me._nom = N
        Me._age = age
        ' opvolging
        Console.Out.WriteLine("Construction personne(string, string, int)")
    End Sub


    Public Sub New(ByVal P As personne)
        ' nog een persoon
        _nbPersonnes += 1
        ' bouw
        Me._prenom = P._prenom
        Me._nom = P._nom
        Me._age = P._age
        ' opvolging
        Console.Out.WriteLine("Construction personne(string, string, int)")
    End Sub

    ' klasse-eigenschap
    Public Shared ReadOnly Property nbPersonnes() As Long
        Get
            Return _nbPersonnes
        End Get
    End Property

    ' instantie-eigenschappen
    Public Property prenom() As String
        Get
            Return _prenom
        End Get
        Set(ByVal Value As String)
            _prenom = Value
        End Set
    End Property

    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = Value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            ' geldige leeftijd?
            If Value >= 0 Then
                _age = Value
            Else
                Throw New Exception("âge (" & Value & ") invalide")
            End If
        End Set
    End Property

    Public ReadOnly Property identite() As String
        Get
            Return "personne(" & _prenom & "," & _nom & "," & age & ")"
        End Get
    End Property
End Class

De methode identifie is vervangen door de alleen-lezen eigenschap identité, die de persoon identificeert. We maken een klasse enseignant die erft van de klasse personne:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Public Class enseignant
    Inherits personne
    ' attributen
    Private _section As Integer

    ' constructor
    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        MyBase.New(P, N, age)
        Me._section = section
        ' tracking
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub

    ' sectie-eigenschap
    Public Property section() As Integer
        Get
            Return _section
        End Get
        Set(ByVal Value As Integer)
            _section = Value
        End Set
    End Property
End Class 

De klasse enseignant voegt het volgende toe aan de methoden en attributen van de klasse personne:

  • een attribuut section, dat het sectienummer is waartoe de docent behoort binnen het docentencorps (grofweg één sectie per vak)
  • een nieuwe constructor waarmee alle attributen van een docent kunnen worden geïnitialiseerd

De declaratie


Public Class enseignant
    Inherits personne

geeft aan dat de klasse enseignant is afgeleid van de klasse personne.

3.2.2. Een docentobject aanmaken

De constructor van de klasse enseignant is als volgt:


    ' constructor
    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        MyBase.New(P, N, age)
        Me._section = section
         ' tracking
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub    

De verklaring


    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)

geeft aan dat de constructor vier parameters ontvangt: P, N, age, section. De constructor moet er drie, namelijk (P,N,age), doorgeven aan zijn basisklasse, in dit geval de klasse personne. We weten dat deze klasse een constructor person(string, string, int) heeft waarmee een persoon kan worden aangemaakt met de doorgegeven parameters (P,N,age). De klasse [enseignant] geeft de parameters (P, N, leeftijd) als volgt door aan haar basisklasse:


        MyBase.New(P, N, age)

Zodra de constructie van de basisklasse is voltooid, wordt de constructie van het object enseignant voortgezet door de uitvoering van de body van de constructor:


        Me._section = section

Kortom, de constructor van een afgeleide klasse:

  • geeft aan de basisklasse de parameters door die deze nodig heeft om zichzelf te construeren
  • gebruikt de overige parameters om de eigen attributen te initialiseren

Men had ook het volgende kunnen schrijven:


    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        Me._prenom = P
        Me._nom = N
        Me._age = age
        Me._section = section
        ' tracking
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub    

Dat is onmogelijk. De klasse personne heeft haar drie velden _prenom, _nom en _age als privé (private) gedeclareerd. Alleen objecten van dezelfde klasse hebben directe toegang tot deze velden. Alle andere objecten, inclusief afgeleide objecten zoals hier, moeten via openbare methoden werken om er toegang toe te krijgen. Dit zou anders zijn geweest als de klasse personne de drie velden als beschermd (protected) had gedeclareerd: dan zou zij afgeleide klassen toestaan om directe toegang te hebben tot de drie velden. In ons voorbeeld was het gebruik van de constructor van de bovenliggende klasse dus de juiste oplossing en dit is de gebruikelijke werkwijze: bij het aanmaken van een onderliggend object roepen we eerst de constructor van het bovenliggende object aan en vullen we vervolgens de initialisaties aan die specifiek zijn voor het onderliggende object (section in ons voorbeeld).

Laten we de klassen personne en enseignant in assemblages compileren:

dos>vbc /t:library personne.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>vbc /r:personne.dll /t:library enseignant.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>dir
25/02/2004  10:08                1 828 personne.vb
25/02/2004  10:11                  675 enseignant.vb
25/02/2004  10:12                  223 test.vb
25/02/2004  10:16                4 096 personne.dll
25/02/2004  10:16                3 584 enseignant.dll

Merk op dat voor het compileren van de dochterklasse enseignant het bestand personne.dll moest worden gerefereerd, dat de klasse personne bevat. Laten we een eerste testprogramma proberen:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test
    Sub Main()
        Console.Out.WriteLine(New enseignant("Jean", "Dupont", 30, 27).identite)
    End Sub
End Module

Dit programma beperkt zich tot het aanmaken van een object enseignant (new) en het identificeren ervan. De klasse enseignant heeft geen methode identité, maar de bovenliggende klasse heeft er wel een, die bovendien openbaar is: door overerving wordt dit een openbare methode van de klasse enseignant. De verkregen resultaten zijn als volgt:

dos>vbc /r:personne.dll /r:enseignant.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573

dos>test
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(Jean,Dupont,30)

We zien dat:

  • een object personne vóór het object enseignant is aangemaakt
  • de verkregen identiteit is die van het object personne

3.2.3. Overbelasting van een methode of eigenschap

In het vorige voorbeeld hadden we de identiteit van het onderdeel personne van de docent, maar er ontbreekt bepaalde informatie die specifiek is voor de klasse enseignant (de sectie). We moeten dus een eigenschap schrijven waarmee de docent kan worden geïdentificeerd:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Public Class enseignant
    Inherits personne
    ' attributen
    Private _section As Integer

    ' constructor
    Public Sub New(ByVal P As String, ByVal N As String, ByVal age As Integer, ByVal section As Integer)
        MyBase.New(P, N, age)
        Me._section = section
        ' tracking
        Console.Out.WriteLine("Construction enseignant(string,string,int,int)")
    End Sub

    ' sectie-eigenschap
    Public Property section() As Integer
        Get
            Return _section
        End Get
        Set(ByVal Value As Integer)
            _section = Value
        End Set
    End Property

    ' overbelasting van de identiteits-eigenschap
    Public Shadows ReadOnly Property identite() As String
        Get
            Return "enseignant(" & MyBase.identite & "," & _section & ")"
        End Get
    End Property
End Class

De methode identite van de klasse enseignant is gebaseerd op de methode identite van de bovenliggende klasse (MyBase.identite) om het gedeelte „personne” weer te geven en vult dit vervolgens aan met het veld _section, dat specifiek is voor de klasse enseignant. Let op de declaratie van de eigenschap identite:


    Public Shadows ReadOnly Property identite() As String

wat aangeeft dat de eigenschap identite de gelijknamige methode „verbergt” die in de bovenliggende klasse zou kunnen bestaan. Stel dat we een object enseignant E hebben. Dit object bevat een object personne:

De eigenschap 'identity' is zowel gedefinieerd in de klasse enseignant als in de bovenliggende klasse personne. In de dochterklasse enseignant moet de eigenschap identite worden voorafgegaan door het sleutelwoord **shadows om aan te geven dat er een nieuwe eigenschap identite wordt gedefinieerd voor de klasse enseignant.


    Public Shadows ReadOnly Property identite() As String

De klasse enseignant beschikt nu over twee eigenschappen identite:

  • de eigenschap die is overgenomen van de bovenliggende klasse personne
  • een eigen eigenschap

Als E een object van het type enseignant is, verwijst E.identite naar de methode identite van de klasse enseignant. Men zegt dat de eigenschap identite van de bovenliggende klasse wordt „herdefinieerd” door de eigenschap identite van de onderliggende klasse. In het algemeen geldt: als O een object is en M een methode, dan zoekt het systeem, om de methode O.M uit te voeren, naar een methode M in de volgende volgorde:

  • in de klasse van het object O
  • in de bovenliggende klasse, indien deze bestaat
  • in de bovenliggende klasse van de bovenliggende klasse, indien deze bestaat
  • enzovoort…

Erfenis maakt het dus mogelijk om in de dochterklasse methoden/eigenschappen met dezelfde naam als in de bovenliggende klasse opnieuw te definiëren. Hierdoor kan de dochterklasse aan haar eigen behoeften worden aangepast. In combinatie met polymorfisme, dat we straks zullen bespreken, is het overschrijven van methoden/eigenschappen het belangrijkste voordeel van erfenis. Laten we hetzelfde voorbeeld als eerder bekijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test
    Sub Main()
        Console.Out.WriteLine(New enseignant("Jean", "Dupont", 30, 27).identite)
    End Sub
End Module

De verkregen resultaten zijn deze keer als volgt:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Jean,Dupont,30),27)

3.2.4. Polymorfisme

Laten we eens kijken naar een reeks klassen: C0 C1 C2 … Waarbij Ci Cj aangeeft dat de klasse Cj is afgeleid van de klasse Ci. Dit houdt in dat de klasse Cj alle kenmerken van de klasse Ci heeft, plus nog enkele andere. Stel dat er objecten Oi van het type Ci zijn. Het is toegestaan om het volgende te schrijven:

    Oi=Oj avec j>i

Door overerving heeft de klasse Cj namelijk alle kenmerken van de klasse Ci plus nog andere. Een object Oj van het type Cj bevat dus een object van het type Ci. De bewerking

Oi=Oj

zorgt ervoor dat Oi een verwijzing is naar het object van het type Ci dat in het object Oj is opgenomen.

Het feit dat een variabele Oi van de klasse Ci in feite niet alleen kan verwijzen naar een object van de klasse Ci, maar ook naar elk object dat is afgeleid van de klasse Ci, wordt polymorfisme genoemd: het vermogen van een variabele om naar verschillende soorten objecten te verwijzen. Laten we een voorbeeld nemen en de volgende functie bekijken, die onafhankelijk is van welke klasse dan ook:


    Sub affiche(ByVal p As personne)

We zouden net zo goed kunnen schrijven

    dim p as personne
    ...
    affiche(p);

als

    dim e as enseignant
    ...
    affiche(e);

In dit laatste geval krijgt de formele parameter van het type personne van de functie affiche een waarde van het type enseignant. Aangezien het type enseignant is afgeleid van het type personne, is dit toegestaan.

3.2.5. Herdefinitie en polymorfisme

Laten we onze procedure affiche aanvullen:


    Sub affiche(ByVal p As personne)
         ' identiteit van p weergeven
        Console.Out.WriteLine(p.identite)
    End Sub    

De methode p.identite retourneert een tekenreeks die het object personne. identificeert. Wat gebeurt er in het geval van ons vorige voorbeeld met een object enseignant:


        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)

Laten we eens naar het volgende voorbeeld kijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test
    Sub Main()
        ' een docent
        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)
        ' een persoon
        Dim p As New personne("Jean", "Dupont", 30)
        affiche(p)
    End Sub

    ' toont
    Sub affiche(ByVal p As personne)
        ' identiteitsposter van p
        Console.Out.WriteLine(p.identite)
    End Sub
End Module

De verkregen resultaten zijn als volgt:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(Lucile,Dumas,56)
Construction personne(string, string, int)
personne(Jean,Dupont,30)

Uit de uitvoering blijkt dat de instructie p.identite telkens de eigenschap identite van een personne heeft uitgevoerd, de persoon in de enseignant e, en vervolgens de personne p zelf. Het heeft zich niet aangepast aan het object dat daadwerkelijk als parameter aan affiche is doorgegeven. We hadden liever de volledige identiteit van de enseignant gehad. Hiervoor had de notatie p.identite moeten verwijzen naar de eigenschap identite van het object waarnaar daadwerkelijk wordt verwezen door p, in plaats van naar de eigenschap identite van het deel "personne" van het object waarnaar p daadwerkelijk verwijst. Dit resultaat kan worden bereikt door identite te declareren als een overschrijfbare eigenschap (overridable) in de basisklasse personne:


    Public Overridable ReadOnly Property identite() As String
        Get
            Return "personne(" & _prenom & "," & _nom & "," & age & ")"
        End Get
    End Property

Het sleutelwoord **overridable maakt van identite een overschrijfbare of virtuele eigenschap. Dit sleutelwoord kan ook op methoden worden toegepast. Afgeleide klassen die een virtuele eigenschap of methode herdefiniëren, moeten dan het sleutelwoord **overrides gebruiken in plaats van shadows om hun herdefinieerde eigenschap/methode aan te duiden. Zo wordt in de klasse enseignant de eigenschap identite als volgt gedefinieerd:


    ' identiteitsattribuut overschrijven
    Public Overrides ReadOnly Property identite() As String
        Get
            Return "enseignant(" & MyBase.identite & "," & _section & ")"
        End Get
    End Property

Het testprogramma:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test
    Sub Main()
        ' een docent
        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)
        ' een persoon
        Dim p As New personne("Jean", "Dupont", 30)
        affiche(p)
    End Sub

    ' toont
    Sub affiche(ByVal p As personne)
        ' identiteitskaart van p
        Console.Out.WriteLine(p.identite)
    End Sub
End Module

levert dan de volgende resultaten op:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Lucile,Dumas,56),61)
Construction personne(string, string, int)
personne(Jean,Dupont,30)

Deze keer hebben we de volledige identiteit van de docent gekregen. Laten we nu eens een methode definiëren in plaats van een eigenschap. De klasse object is de "bovenliggende" klasse van alle VB.NET-klassen. Dus wanneer we schrijven:

    public class personne

schrijven we impliciet:

    public class personne 
         inherits object

De klasse object definieert een virtuele methode ToString:

Image

De methode ToString retourneert de naam van de klasse waartoe het object behoort, zoals in het volgende voorbeeld te zien is:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test2
    Sub Main()
        ' een docent
        Console.Out.WriteLine(New enseignant("Lucile", "Dumas", 56, 61).ToString())
        ' een persoon
        Console.Out.WriteLine(New personne("Jean", "Dupont", 30).ToString())
    End Sub
End Module

De gegenereerde resultaten zijn als volgt:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant
Construction personne(string, string, int)
personne

Opgemerkt moet worden dat, hoewel we de methode ToString niet opnieuw hebben gedefinieerd in de klassen personne en enseignant, kunnen we toch vaststellen dat de methode ToString van de klasse object toch in staat is om de werkelijke klassenaam van het object weer te geven. Laten we de methode ToString in de klassen personne en enseignant opnieuw definiëren:


    ' ToString
    Public Overrides Function ToString() As String
        ' we maken de eigenschap identiteit
        Return identite
    End Function

De definitie is in beide klassen hetzelfde. Laten we het volgende testprogramma eens bekijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test
    Sub Main()
        ' een docent
        Dim e As New enseignant("Lucile", "Dumas", 56, 61)
        affiche(e)
        ' een persoon
        Dim p As New personne("Jean", "Dupont", 30)
        affiche(p)
    End Sub

    ' toont
    Sub affiche(ByVal p As personne)
        ' identiteitskaart van p
        Console.Out.WriteLine(p.identite)
    End Sub
End Module

De uitvoerresultaten zijn als volgt:

Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
enseignant(personne(Lucile,Dumas,56),61)
Construction personne(string, string, int)
personne(Jean,Dupont,30)

3.3. Een indexer voor een klasse definiëren

Laten we eens kijken naar de vooraf gedefinieerde klasse [ArrayList] in het platform .NET. Met deze klasse kunnen objecten in een lijst worden opgeslagen. Ze behoort tot de naamruimte [System.Collections]. In het volgende voorbeeld gebruiken we deze klasse om een lijst met personen (in de breedste zin van het woord) op te slaan:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System
Imports System.Collections

' klasse listeDePersonnes
Public Class listeDePersonnes
    Inherits ArrayList

    ' om een persoon aan de lijst toe te voegen
    Public Overloads Sub Add(ByVal p As personne)
        MyBase.Add(p)
    End Sub

    ' een indexeerder
    Default Public Shadows Property Item(ByVal i As Integer) As personne
        Get
            Return CType(MyBase.Item(i), personne)
        End Get
        Set(ByVal Value As personne)
            MyBase.Item(i) = Value
        End Set
    End Property

    ' een andere indexeerder
    Default Public Shadows ReadOnly Property Item(ByVal N As String) As Integer
        Get
            ' we zoeken de persoon met de naam N
            Dim i As Integer
            For i = 0 To Count - 1
                If CType(Me(i), personne).nom = N Then
                    Return i
                End If
            Next i
            Return -1
        End Get
    End Property

    ' toString
    Public Overrides Function ToString() As String
        ' geeft (element1, element2, ..., elementn) terug
        Dim liste As String = "("
        Dim i As Integer
        ' de dynamische array wordt doorlopen
        For i = 0 To (Count - 2)
            liste += "[" + Me(i).ToString + "]" + ","
        Next i        'for
        ' laatste element
        If Count <> 0 Then
            liste += "[" + Me(i).ToString + "]"
        End If
        liste += ")"
        Return liste
    End Function
End Class

Laten we wat informatie geven over enkele eigenschappen en methoden van de klasse [ArrayList]:

Count
attribuut dat het aantal elementen in de lijst aangeeft
Add(Object)
methode waarmee een object aan de lijst kan worden toegevoegd
Item(Integer i)
methode die het i-de element van de lijst oplevert

We merken op dat we, om element nr. i van de lijst op te halen, niet [liste.Item(i)] hebben geschreven, maar direct [liste(i)], wat op het eerste gezicht onjuist lijkt. Dit is echter mogelijk omdat de klasse [ArrayList] een standaard-eigenschap [Item] definieert volgens een syntaxis die vergelijkbaar is met de volgende:


    Default Public Property Item(ByVal i As Integer) As Object
        Get
...
        End Get
        Set(ByVal Value As personne)
...
        End Set    
    End Property

Wanneer de compiler de notatie [liste(i)] tegenkomt, controleert hij of de klasse [ArrayList] een eigenschap heeft gedefinieerd met de volgende handtekening:


    Default Public Property Proc(ByVal var As Integer) As Type

Hier vindt hij de procedure [Item]. Vervolgens vertaalt hij de notatie [liste(i)] naar [liste.Item(i)]. De eigenschap [Item] wordt de standaard geïndexeerde eigenschap van de klasse [ArrayList] genoemd. De uitvoering van het voorgaande programma levert de volgende resultaten op:

dos>vbc /r:personne.dll /r:enseignant.dll lstpersonnes1.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:34             3 584 lstpersonnes1.exe
12/03/2004  13:39               661 lstpersonnes1.vb
11/03/2004  18:26             4 096 personne.dll
dos>lstpersonnes1
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
personne(paul,chenou,31)
personne(nicole,chenou,11)
enseignant(personne(jacques,sileau,33),61)

Laten we een klasse aanmaken met de naam [listeDePersonnes], die een lijst met personen zou zijn, dus een specifieke lijst die logischerwijs kan worden afgeleid van de klasse [ArrayList]:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System
Imports System.Collections

' klasse listeDePersonnes
Public Class listeDePersonnes
    Inherits ArrayList

    ' om een persoon aan de lijst toe te voegen
    Public Shadows Sub Add(ByVal p As Object)
        ' moet p een persoon zijn
        If Not TypeOf (p) Is personne Then
            Throw New Exception("L'objet ajouté (" + p.ToString + ") n'est pas une personne")
        Else
            MyBase.Add(p)
        End If
    End Sub

    ' een indexeerder
    Default Public Shadows Property Index(ByVal i As Integer) As personne
        Get
            Return CType(MyBase.Item(i), personne)
        End Get
        Set(ByVal Value As personne)
            MyBase.Item(i) = Value
        End Set
    End Property

    ' toString
    Public Overrides Function ToString() As String
        ' geeft (element1, element2, ..., elementn) terug
        Dim liste As String = "("
        Dim i As Integer
        ' de dynamische array wordt doorlopen
        For i = 0 To (Count - 2)
            liste += "[" + Me(i).ToString + "]" + ","
        Next i        'for
        ' laatste element
        If Count <> 0 Then
            liste += "[" + Me(i).ToString + "]"
        End If
        liste += ")"
        Return liste
    End Function
End Class

De klasse bevat de volgende methoden en eigenschappen:

ToString
geeft een tekenreeks terug die de inhoud van de lijst „weergeeft“
Add(personne)
methode waarmee een persoon aan de lijst kan worden toegevoegd
Item(Integer i)
standaard geïndexeerde eigenschap die persoon i uit de lijst opgeeft

Laten we eens kijken naar de nieuwe punten:

Er is een nieuwe methode [Add] aangemaakt.


     ' om een persoon aan de lijst toe te voegen
    Public Shadows Sub Add(ByVal p As Object)
        ' moet p een persoon zijn
        If Not TypeOf (p) Is personne Then
            Throw New Exception("L'objet ajouté (" + p.ToString + ") n'est pas une personne")
        Else
            MyBase.Add(p)
        End If
    End Sub

Er bestaat al een procedure in de bovenliggende klasse [ArrayList] met dezelfde handtekening; vandaar het trefwoord [Shadows] om aan te geven dat de nieuwe procedure die van de bovenliggende klasse vervangt. De methode [Add] van de dochterklasse controleert of het toegevoegde object inderdaad van het type [personne] is of daarvan is afgeleid met de functie [TypeOf]. Als dit niet het geval is, wordt er een uitzondering gegenereerd met de instructie [Throw]. Als het toegevoegde object wel van het type [personne] is, wordt de methode [Add] van de basisklasse gebruikt om de toevoeging uit te voeren.

Er wordt een geïndexeerde eigenschap aangemaakt:


    ' een indexer
    Default Public Shadows Property Index(ByVal i As Integer) As personne
        Get
            Return CType(MyBase.Item(i), personne)
        End Get
        Set(ByVal Value As personne)
            MyBase.Item(i) = Value
        End Set
    End Property

Er bestaat al een standaardeigenschap met de naam [Item] in de basisklasse met dezelfde handtekening. Daarom moet het sleutelwoord [Shadows] worden gebruikt om aan te geven dat de nieuwe geïndexeerde eigenschap [Index] die van de basisklasse [Item] zal "verbergen". Merk op dat dit zelfs geldt als de twee eigenschappen niet dezelfde naam hebben. Met de eigenschap [Index] kan naar persoon nr. i in de lijst worden verwezen. Deze eigenschap is gebaseerd op de eigenschap [Item] van de basisklasse om toegang te krijgen tot element nr. i van het onderliggende object [ArrayList]. Er worden typewijzigingen doorgevoerd om er rekening mee te houden dat de eigenschap [Item] werkt met elementen van het type [Object], terwijl de eigenschap [Index] werkt met elementen van het type [personne].

Ten slotte herdefiniëren we (Overrides) de methode [ToString] van de klasse [ArrayList]:


    ' toString
    Public Overrides Function ToString() As String
        ' geeft (element1, element2, ..., elementn) terug
        Dim liste As String = "("
        Dim i As Integer
        ' we doorlopen de dynamische array
        For i = 0 To (Count - 2)
            liste += "[" + Me(i).ToString + "]" + ","
        Next i        'for
         ' laatste element
        If Count <> 0 Then
            liste += "[" + Me(i).ToString + "]"
        End If
        liste += ")"
        Return liste
    End Function

Deze methode retourneert een tekenreeks in de vorm "(e1,e2,...,en)", waarbij ei de elementen van de lijst zijn. Let op de notatie [Me(i)], die verwijst naar element nr. i van het huidige object [Me]. Hierbij wordt de standaard geïndexeerde eigenschap gebruikt. [Me(i)] is dus gelijk aan [Me.Index(i)].

De klassecode wordt in het bestand [lstpersonnes2.vb] geplaatst en gecompileerd:

dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:45               970 lstpersonnes2.vb
11/03/2004  18:26             4 096 personne.dll
dos>vbc /r:personne.dll /r:enseignant.dll /t:library lstpersonnes2.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:50             3 584 lstpersonnes2.dll
12/03/2004  15:45               970 lstpersonnes2.vb
11/03/2004  18:26             4 096 personne.dll

Er wordt een testprogramma opgesteld:


' opties
Option Explicit On 
Option Strict On

' naamruimten
Imports System
Imports System.Collections

Module test
    Sub Main()
        ' een lege lijst met personen aanmaken
        Dim liste As listeDePersonnes = New listeDePersonnes
        ' personen aanmaken
        Dim p1 As personne = New personne("paul", "chenou", 31)
        Dim p2 As personne = New personne("nicole", "chenou", 11)
        Dim e1 As enseignant = New enseignant("jacques", "sileau", 33, 61)
        ' de lijst vullen
        liste.Add(p1)
        liste.Add(p2)
        liste.Add(e1)
        ' de lijst weergeven
        Console.Out.WriteLine(liste.ToString)
        ' een ander object dan een persoon toevoegen
        Try
            liste.Add(4)
        Catch e As Exception
            Console.Error.WriteLine(e.Message)
        End Try
    End Sub
End Module

Het wordt gecompileerd:

dos>vbc /r:personne.dll /r:enseignant.dll /r:lstpersonnes2.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
11/03/2004  18:26             3 584 enseignant.dll
12/03/2004  15:50             3 584 lstpersonnes2.dll
12/03/2004  15:45               970 lstpersonnes2.vb
11/03/2004  18:26             4 096 personne.dll
12/03/2004  15:50             3 584 test.exe
12/03/2004  15:49               623 test.vb

en vervolgens uitgevoerd:

dos>test
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction personne(string, string, int)
Construction enseignant(string,string,int,int)
([personne(paul,chenou,31)],[personne(nicole,chenou,11)],[enseignant(personne(jacques,sileau,33),61)])
L'objet ajouté (4) n'est pas une personne

Men zou kunnen willen schrijven

dim p as personne=l("nom")

waarbij l van het type [listeDePersonnes] zou zijn. Hier willen we de lijst l niet langer op een elementnummer indexeren, maar op een persoonsnaam. Hiervoor definiëren we een nieuwe, standaard geïndexeerde eigenschap:


    ' een andere indexeerder
    Default Public Shadows ReadOnly Property Item(ByVal N As String) As Integer
        Get
            ' zoeken naar de persoon met de naam N
            Dim i As Integer
            For i = 0 To Count - 1
                If CType(Me(i), personne).nom = N Then
                    Return i
                End If
            Next i
            Return -1
        End Get
    End Property

De eerste regel


    Default Public Shadows ReadOnly Property Index(ByVal N As String) As Integer

geeft aan dat er opnieuw een standaard geïndexeerde eigenschap wordt aangemaakt. Alle standaardeigenschappen moeten dezelfde naam hebben, in dit geval [Index]. De nieuwe eigenschap [Index] indexeert de klasse listeDePersonnes op basis van de tekenreeks N. Het resultaat van listeDePersonnes(N) is een geheel getal. Dit gehele getal is de positie in de lijst van de persoon met de naam N, of -1 als deze persoon niet in de lijst voorkomt. We definiëren alleen de eigenschap get, waardoor het schrijven van listeDePersonnes wordt verhinderd ("nom")=valeur zou de definitie van de eigenschap set hebben vereist). Vandaar het sleutelwoord [ReadOnly]. Het sleutelwoord [Shadows] is nodig om de standaard-eigenschap van de basisklasse te verbergen (hoewel deze niet dezelfde signatuur heeft).

In de body van get wordt de lijst met personen doorlopen op zoek naar de naam N die als parameter is doorgegeven. Als deze op positie i wordt gevonden, wordt i geretourneerd; anders wordt -1 geretourneerd.

Een nieuw testprogramma zou er als volgt uit kunnen zien:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' testpagina
Module test
    Sub Main()
        ' een lijst met personen
        Dim l As New listeDePersonnes
        ' personen toevoegen
        l.Add(New personne("jean", "dumornet", 10))
        l.Add(New personne("pauline", "duchemin", 12))
        ' weergave
        Console.Out.WriteLine(("l=" + l.ToString))
        l.Add(New personne("jacques", "tartifume", 27))
        Console.Out.WriteLine(("l=" + l.ToString))
        ' element 1 wijzigen
        l(1) = New personne("sylvie", "cachan", 5)
        ' element 1 weergeven
        Console.Out.WriteLine(("l[1]=" + l(1).ToString))
        ' lijst weergeven l
        Console.Out.WriteLine(("l=" + l.ToString))
        ' zoeken naar personen
        Dim noms() As String = New [String]() {"cachan", "inconnu"}
        Dim i As Integer
        For i = 0 To noms.Length - 1
            Dim inom As Integer = l(noms(i))
            If inom <> -1 Then
                Console.Out.WriteLine(("personne(" & noms(i) & ")=" & l(inom).ToString))
            Else
                Console.Out.WriteLine(("personne(" + noms(i) + ") n'existe pas"))
            End If
        Next i
    End Sub    
End Module

De uitvoering levert de volgende resultaten op:

personne(string, string, int)
Construction personne(string, string, int)
l=([personne(jean,dumornet,10)],[personne(pauline,duchemin,12)])
Construction personne(string, string, int)
l=([personne(jean,dumornet,10)],[personne(pauline,duchemin,12)],[personne(jacques,tartifume,27)])
Construction personne(string, string, int)
l[1]=personne(sylvie,cachan,5)
l=([personne(jean,dumornet,10)],[personne(sylvie,cachan,5)],[personne(jacques,tartifume,27)])
personne(cachan)=personne(sylvie,cachan,5)
personne(inconnu) n'existe pas

3.4. De structuren

De structuur VB.NET is rechtstreeks afgeleid van de structuur in de programmeertaal C en lijkt sterk op een klasse. Een structuur wordt als volgt gedefinieerd:


Structure spersonne
' kenmerken
    ...
' eigenschappen
...
' constructors
...
' methoden
End Structure

Ondanks de gelijkenis in de syntaxis zijn er belangrijke verschillen tussen klassen en structuren. Het begrip overerving bestaat bijvoorbeeld niet bij structuren. Als we een klasse schrijven die niet afgeleid mag worden, welke verschillen tussen structuren en klassen helpen ons dan bij de keuze tussen beide? Laten we het volgende voorbeeld gebruiken om dit te ontdekken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' structuur spersonne
Structure spersonne
    Public nom As String
    Public age As Integer
End Structure

' klasse cpersonne
Class cpersonne
    Public nom As String
    Public age As Integer
End Class

' een testmodule
Public Module test
    Sub Main()
        ' een persoon p1
        Dim sp1 As spersonne
        sp1.nom = "paul"
        sp1.age = 10
        Console.Out.WriteLine(("sp1=spersonne(" & sp1.nom & "," & sp1.age & ")"))
        ' een persoon p2
        Dim sp2 As spersonne = sp1
        Console.Out.WriteLine(("sp2=spersonne(" & sp2.nom & "," & sp2.age & ")"))
        ' sp2 is gewijzigd
        sp2.nom = "nicole"
        sp2.age = 30
        ' controle van sp1 en sp2
        Console.Out.WriteLine(("sp1=cpersonne(" & sp1.nom & "," & sp1.age & ")"))
        Console.Out.WriteLine(("sp2=cpersonne(" & sp2.nom & "," & sp2.age & ")"))

        ' een c-persoon cp1
        Dim cp1 As New cpersonne
        cp1.nom = "paul"
        cp1.age = 10
        Console.Out.WriteLine(("cP1=cpersonne(" & cp1.nom & "," & cp1.age & ")"))
        ' een cpersoon P2
        Dim cp2 As cpersonne = cp1
        Console.Out.WriteLine(("cP2=cpersonne(" & cp2.nom & "," & cp2.age & ")"))
        ' P2 is gewijzigd
        cp2.nom = "nicole"
        cp2.age = 30
        ' controle van P1 en P2
        Console.Out.WriteLine(("cP1=cpersonne(" & cp1.nom & "," & cp1.age & ")"))
        Console.Out.WriteLine(("cP2=cpersonne(" & cp2.nom & "," & cp2.age & ")"))
    End Sub
End Module

Als je dit programma uitvoert, krijg je de volgende resultaten:

sp1=spersonne(paul,10)
sp2=spersonne(paul,10)
sp1=cpersonne(paul,10)
sp2=cpersonne(nicole,30)
cP1=cpersonne(paul,10)
cP2=cpersonne(paul,10)
cP1=cpersonne(nicole,30)
cP2=cpersonne(nicole,30)

Waar op de voorgaande pagina's van dit hoofdstuk de klasse personne werd gebruikt, gebruiken we nu de structuur spersonne:


' structuur van de persoon
Structure spersonne
    Public nom As String
    Public age As Integer
End Structure

De verklaring


        Dim sp1 As spersonne

creëert een structuur (naam, leeftijd) en de waarde van sp1 is deze structuur zelf.

De declaratie


        Dim cp1 As New cpersonne

maakt een object [cpersonne] aan (grofweg het equivalent van onze structuur) en cp1 is dan het adres (de referentie) van dit object.

Laten we het samenvatten

  • in het geval van de structuur is de waarde van sp1 de structuur zelf
  • in het geval van de klasse is de waarde van p1 het adres van het aangemaakte object

Wanneer in het programma wordt geschreven


        Dim sp2 As spersonne = sp1

wordt er een nieuwe structuur (naam, leeftijd) aangemaakt en geïnitialiseerd met de waarde van p1, dus de structuur zelf.

De structuur van sp1 wordt dus gedupliceerd in sp2. Dit is een kopie van de waarde.

De instructie


        Dim cp2 As cpersonne = cp1

werkt anders. De waarde van cp1 wordt gekopieerd naar cp2, maar aangezien deze waarde in feite het adres van het object is, wordt het object zelf niet gedupliceerd. Er zijn gewoon twee verwijzingen naar het object:

In het geval van de structuur: als we de waarde van sp2 wijzigen, wijzigen we de waarde van sp1 niet, wat het programma laat zien. In het geval van het object: als het object waarnaar cp2 verwijst, wordt gewijzigd, wordt het object waarnaar cp1 verwijst ook gewijzigd, aangezien het om hetzelfde object gaat. Dit blijkt ook uit de resultaten van het programma.

Uit deze uitleg kunnen we dus concluderen dat:

  • de waarde van een variabele van het type structuur de structuur zelf is
  • de waarde van een variabele van het type object is het adres van het object waarnaar wordt verwezen

Zodra dit fundamentele verschil begrepen is, blijkt de structuur sterk op de klasse te lijken, zoals het volgende nieuwe voorbeeld laat zien:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' persoonsstructuur
Structure personne
    ' attributen
    Private _nom As String
    Private _age As Integer

    ' eigenschappen
    Public Property nom() As String
        Get
            Return _nom
        End Get
        Set(ByVal Value As String)
            _nom = value
        End Set
    End Property

    Public Property age() As Integer
        Get
            Return _age
        End Get
        Set(ByVal Value As Integer)
            _age = value
        End Set
    End Property

    ' Constructor
    Public Sub New(ByVal NOM As String, ByVal AGE As Integer)
        _nom = NOM
        _age = AGE
    End Sub    'New

    ' TOSTRING
    Public Overrides Function ToString() As String
        Return "personne(" & nom & "," & age & ")"
    End Function
End Structure

' een testmodule
Module test
    Sub Main()
        ' een persoon p1
        Dim p1 As New personne("paul", 10)
        Console.Out.WriteLine(("p1=" & p1.ToString))
        ' een persoon p2
        Dim p2 As personne = p1
        Console.Out.WriteLine(("p2=" & p2.ToString))
        ' p2 is gewijzigd
        p2.nom = "nicole"
        p2.age = 30
        ' controle van p1 en p2
        Console.Out.WriteLine(("p1=" & p1.ToString))
        Console.Out.WriteLine(("p2=" & p2.ToString))
    End Sub
End Module

We krijgen de volgende uitvoerresultaten:

p1=personne(paul,10)
p2=personne(paul,10)
p1=personne(paul,10)
p2=personne(nicole,30)

Het enige opvallende verschil tussen structuur en klasse is dat bij een klasse de objecten p1 en p2 aan het einde van het programma dezelfde waarde zouden hebben gehad, namelijk die van p2.

3.5. Interfaces

Een interface is een verzameling prototypes van methoden of eigenschappen die samen een contract vormen. Een klasse die besluit een interface te implementeren, verbindt zich ertoe een implementatie te leveren van alle methoden die in de interface zijn gedefinieerd. De compiler controleert deze implementatie. Hier volgt bijvoorbeeld de definitie van de interface Istats:

Public Interface Istats
    Function moyenne() As Double
    Function écartType() As Double
End Interface

Elke klasse die deze interface implementeert, wordt gedeclareerd als

public class C 
 Implements Istats
...
function moyenne() as Double Implements Istats.moyenne
...
end function
function écartType () as Double Implements Istats. écartType
...
end function
end class

De methoden [moyenne] en [écartType] moeten in klasse C worden gedefinieerd. Laten we de volgende code eens bekijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' structuur
Public Structure élève
    Public _nom As String
    Public _note As Double

    ' constructor
    Public Sub New(ByVal NOM As String, ByVal NOTE As Double)
        Me._nom = NOM
        Me._note = NOTE
    End Sub
End Structure

' klasse notes
Public Class notes
    ' attribuut
    Protected _matière As String
    Protected _élèves() As élève

    ' fabrikant
    Public Sub New(ByVal MATIERE As String, ByVal ELEVES() As élève)
        ' opslag van leerlingen en vakken
        Me._matière = MATIERE
        Me._élèves = ELEVES
    End Sub

    ' ToString
    Public Overrides Function ToString() As String
        Dim valeur As String = "matière=" + _matière + ", notes=("
        Dim i As Integer
        ' alle cijfers worden samengevoegd
        For i = 0 To (_élèves.Length - 1) - 1
            valeur &= "[" & _élèves(i)._nom & "," & _élèves(i)._note & "],"
        Next i
        'laatste cijfer
        If _élèves.Length <> 0 Then
            valeur &= "[" & _élèves(i)._nom & "," & _élèves(i)._note & "]"
        End If
        valeur += ")"
        ' einde
        Return valeur
    End Function
End Class

De klas notes verzamelt de cijfers van een klas in een vak:


Public Class notes
     ' attribuut
    Protected _matière As String
    Protected _élèves() As élève

De attributen worden gedeclareerd als protected, zodat ze toegankelijk zijn vanuit een afgeleide klasse. Het type élève is een structuur waarin de naam van de leerling en zijn cijfer voor het vak worden opgeslagen:


Public Structure élève
    Public _nom As String
    Public _note As Double

     ' constructor
    Public Sub New(ByVal NOM As String, ByVal NOTE As Double)
        Me._nom = NOM
        Me._note = NOTE
    End Sub    
End Structure

We besluiten deze klasse notes af te leiden naar een klasse notesStats met twee extra attributen: het gemiddelde en de standaardafwijking van de cijfers:


Public Class notesStats
    Inherits notes
    Implements Istats

     ' attributen
    Private _moyenne As Double
    Private _écartType As Double

De klasse notesStats implementeert de volgende interface Istats:

Public Interface Istats
    Function moyenne() As Double
    Function écartType() As Double
End Interface

Dit betekent dat de klasse notesStats twee methoden moet hebben met de namen moyenne en écartType, met de handtekening zoals aangegeven in de interface Istats. De klasse notesStats ziet er als volgt uit:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Public Class notesStats
    Inherits notes
    Implements Istats

    ' attributen
    Private _moyenne As Double
    Private _écartType As Double

    ' constructor
    Public Sub New(ByVal MATIERE As String, ByVal ELEVES() As élève)
        MyBase.New(MATIERE, ELEVES)
        ' berekening gemiddelde van de scores
        Dim somme As Double = 0
        Dim i As Integer
        For i = 0 To ELEVES.Length - 1
            somme += ELEVES(i)._note
        Next i
        If ELEVES.Length <> 0 Then
            _moyenne = somme / ELEVES.Length
        Else
            _moyenne = -1
        End If
        ' standaardafwijking
        Dim carrés As Double = 0
        For i = 0 To ELEVES.Length - 1
            carrés += Math.Pow(ELEVES(i)._note - _moyenne, 2)
        Next i
        If ELEVES.Length <> 0 Then
            _écartType = Math.Sqrt((carrés / ELEVES.Length))
        Else
            _écartType = -1
        End If
    End Sub

    ' ToString
    Public Overrides Function ToString() As String
        Return MyBase.ToString() & ",moyenne=" & _moyenne & ",écart-type=" & _écartType
    End Function    'ToString

    ' methoden van de Istats-interface
    Public Function moyenne() As Double Implements Istats.moyenne
        ' geeft het gemiddelde van de scores weer
        Return _moyenne
    End Function

    Public Function écartType() As Double Implements Istats.écartType
        ' geeft de standaardafwijking weer
        Return _écartType
    End Function
End Class

Het gemiddelde _moyenne en de standaardafwijking _ecartType worden berekend zodra het object wordt aangemaakt. Daarom hoeven de methoden moyenne en écartType alleen de waarde van de attributen _moyenne en _ecartType terug te geven. Beide methoden geven -1 terug als de tabel met leerlingen leeg is.

De volgende testklasse:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Module test
    Sub Main()
        ' enkele leerlingen & cijfers
        Dim ELEVES() As élève = {New élève("paul", 14), New élève("nicole", 16), New élève("jacques", 18)}
        ' die worden opgeslagen in een notenobject
        Dim anglais As New notes("anglais", ELEVES)
        ' en die worden weergegeven
        Console.Out.WriteLine((anglais.ToString))
        ' idem met gemiddelde en standaardafwijking
        anglais = New notesStats("anglais", ELEVES)
        Console.Out.WriteLine((anglais.ToString))
    End Sub
End Module

levert de volgende resultaten op:

matière=anglais, notes=([paul,14],[nicole,16],[jacques,18])
matière=anglais, notes=([paul,14],[nicole,16],[jacques,18]),moyenne=16,écart-type=1,63299316185545

De klasse notesStats had de methoden moyenne en écartType heel goed voor zichzelf kunnen implementeren zonder aan te geven dat ze de interface Istats implementeerde. Wat is het nut van interfaces? Het is als volgt: een functie kan als formele parameter een waarde van het type van een interface I accepteren. Elk object van een klasse C dat de interface I implementeert, kan dan als effectieve parameter van deze functie dienen. Laten we het volgende voorbeeld bekijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' een interface Ivoorbeeld
Public Interface Iexemple
    Function ajouter(ByVal i As Integer, ByVal j As Integer) As Integer
    Function soustraire(ByVal i As Integer, ByVal j As Integer) As Integer
End Interface

' een eerste klasse
Public Class classe1
    Implements Iexemple

    Public Function ajouter(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.ajouter
        Return a + b + 10
    End Function

    Public Function soustraire(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.soustraire
        Return a - b + 20
    End Function
End Class

'een tweede klasse
Public Class classe2
    Implements Iexemple

    Public Function ajouter(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.ajouter
        Return a + b + 100
    End Function

    Public Function soustraire(ByVal a As Integer, ByVal b As Integer) As Integer Implements Iexemple.soustraire
        Return a - b + 200
    End Function
End Class

De interface Iexemple definieert twee methoden: ajouter en soustraire. De klassen classe1 en classe2 implementeren deze interface. Merk op dat deze klassen verder niets doen, dit om het voorbeeld eenvoudig te houden. Laten we nu het volgende voorbeeld bekijken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' testklasse
Module test

    'berekenen
    Sub calculer(ByVal i As Integer, ByVal j As Integer, ByVal inter As Iexemple)
        Console.Out.WriteLine(inter.ajouter(i, j))
        Console.Out.WriteLine(inter.soustraire(i, j))
    End Sub

    ' de functie Main
    Sub Main()
        ' aanmaken van twee objecten klasse1 en klasse2
        Dim c1 As New classe1
        Dim c2 As New classe2
        ' aanroepen van de statische functie berekenen
        calculer(4, 3, c1)
        calculer(14, 13, c2)
    End Sub
End Module

De functie calculer accepteert als parameter een element van het type Iexemple. Deze functie kan voor deze parameter dus zowel een object van het type classe1 als van het type classe2 ontvangen. Dit wordt gedaan in de procedure Main, met de volgende resultaten:

17
21
127
201

We zien dus dat we hier te maken hebben met een eigenschap die lijkt op het polymorfisme dat we bij klassen hebben gezien. Als een verzameling klassen Ci die niet via overerving met elkaar verbonden zijn (waardoor we geen gebruik kunnen maken van het polymorfisme van overerving) een verzameling methoden met dezelfde signatuur bevat, kan het interessant zijn om deze methoden te bundelen in een interface I waarvan alle betrokken klassen zouden erven. Instanties van deze klassen Ci kunnen dan worden gebruikt als parameters van functies die een parameter van het type I toelaten, c.a.d. Dit zijn functies die uitsluitend gebruikmaken van de methoden van de Ci-objecten die in de interface I zijn gedefinieerd, en niet van de specifieke attributen en methoden van de verschillende klassen Ci. Merk ten slotte op dat er sprake kan zijn van meervoudige overerving van interfaces, c.a.d. men kan schrijven


Public Class classe
    Implements I1,I2,...

waarbij de Ij interfaces zijn.

3.6. Naamruimten

Om een regel op het scherm weer te geven, gebruiken we de instructie

Console.Out.WriteLine(...)

Als we kijken naar de definitie van de klasse Console


Namespace: System
Assembly: Mscorlib (in Mscorlib.dll)

zien we dat deze deel uitmaakt van de naamruimte System. Dit betekent dat de klasse Console aangeduid zou moeten worden met System.Console en dat we in feite het volgende zouden moeten schrijven:

System.Console.Out.WriteLine(...)

Dit kan worden voorkomen door een clausule imports te gebruiken:

imports System
...
Console.Out.WriteLine(...)

We zeggen dat we de naamruimte System importeren met de clausule imports. Wanneer de compiler de naam van een klasse tegenkomt (in dit geval Console), zal hij deze zoeken in de verschillende naamruimten die zijn geïmporteerd via de clausules imports. Hier vindt hij de klasse Console in de naamruimte System. Laten we nu eens kijken naar de tweede informatie die aan de klasse Console is gekoppeld:


Assembly: Mscorlib (in Mscorlib.dll)

Deze regel geeft aan in welke „assemblage“ de definitie van de klasse Console zich bevindt. Wanneer men buiten Visual Studio.NET compileert en de verwijzingen moet opgeven naar de verschillende dll-bestanden die de te gebruiken klassen bevatten, kan deze informatie nuttig zijn. Ter herinnering: om de dll te refereren die nodig zijn voor het compileren van een klasse, schrijft men:

vbc /r:fic1.dll /r:fic2.dll ... prog.vb

Wanneer men een klasse aanmaakt, kan men deze binnen een naamruimte aanmaken. Het doel van deze naamruimten is om naamconflicten tussen klassen te voorkomen, bijvoorbeeld wanneer deze worden verkocht. Laten we twee bedrijven nemen, E1 en E2, die respectievelijk klassen distribueren die zijn verpakt in de dll, E1.dll en E2.dll. Stel dat een klant C deze twee sets klassen koopt, waarin beide bedrijven een klasse personne hebben gedefinieerd. Klant C compileert een programma als volgt:

vbc /r:E1.dll /r:E2.dll prog.vb

Als de broncode prog.vb gebruikmaakt van de klasse personne, weet de compiler niet of hij de klasse personne uit E1.dll of die uit E2.dll moet nemen. Hij zal een foutmelding geven. Als het bedrijf E1 ervoor zorgt dat het zijn klassen aanmaakt in een naamruimte met de naam E1 en het bedrijf E2 in een naamruimte met de naam E2, zullen de twee klassen personne dan E1.personne en E2.personne heten. De klant moet in zijn klassen ofwel E1.personne, ofwel E2.personne gebruiken, maar niet personne. De naamruimte maakt het mogelijk om deze dubbelzinnigheid op te heffen. Om een klasse in een naamruimte aan te maken, schrijft men:


Namespace istia.st
    Public Class personne
        ' definitie van de klasse
...
  end Class
end Namespace

Laten we ter illustratie in een naamruimte onze eerder besproken klasse personne aanmaken. We kiezen istia.st als naamruimte. De klasse personne wordt dan:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

' aanmaken van de naamruimte istia.st
Namespace istia.st
    Public Class personne
        ' attributen
        Private prenom As String
        Private nom As String
        Private age As Integer

        ' methode
        Public Sub initialise(ByVal P As String, ByVal N As String, ByVal age As Integer)
            Me.prenom = P
            Me.nom = N
            Me.age = age
        End Sub

        ' methode
        Public Sub identifie()
            Console.Out.WriteLine((prenom & "," & nom & "," & age))
        End Sub
    End Class
End Namespace

Deze klasse wordt gecompileerd tot personne.dll:

dos>dir
11/03/2004  18:27               610 personne.vb
dos>vbc /t:library personne.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004  18:06             3 584 personne.dll
11/03/2004  18:27               610 personne.vb

Laten we nu de klasse personne in een testklasse gebruiken:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System
Imports istia.st

' testpagina
Public Module test
    Sub Main()
        Dim p1 As New personne
        p1.initialise("Jean", "Dupont", 30)
        p1.identifie()
    End Sub
End Module

Om te voorkomen dat we


        Dim p1 As New istia.st.personne

hebben we de naamruimte istia.st geïmporteerd met een imports-clausule:


Imports istia.st

Laten we nu het testprogramma compileren:

dos>dir
12/03/2004  18:06             3 584 personne.dll
11/03/2004  18:27               610 personne.vb
12/03/2004  18:05               254 test.vb
dos>vbc /r:personne.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004  18:06             3 584 personne.dll
11/03/2004  18:27               610 personne.vb
12/03/2004  18:08             3 072 test.exe
12/03/2004  18:05               254 test.vb

Dit levert een bestand op met de naam test.exe, dat bij uitvoering de volgende resultaten oplevert:

Jean,Dupont,30

3.7. Het voorbeeld IMPOTS

We nemen de belastingberekening die we in het vorige hoofdstuk al hebben bekeken en verwerken deze met behulp van een klasse. Laten we het probleem nog eens op een rijtje zetten:

We gaan uit van het vereenvoudigde geval van een belastingplichtige die alleen zijn salaris hoeft aan te geven:

  • we berekenen het aantal aandelen van de werknemer nbParts=nbEnfants/2 +1 als hij ongehuwd is, nbEnfants/2+2 als hij getrouwd is, waarbij nbEnfants het aantal kinderen is.
  • als hij ten minste drie kinderen heeft, krijgt hij een half deel extra
  • zijn belastbaar inkomen wordt berekend als R = 0,72 * S, waarbij S zijn jaarsalaris is
  • we berekenen zijn gezinscoëfficiënt QF = R / nbParts
  • zijn belasting I wordt berekend. Laten we de volgende tabel bekijken:
12620,0
0
0
13190
0,05
631
15640
0,1
1290,5
24.740
0,15
2072,5
31810
0,2
3309,5
39.970
0,25
4900
48360
0,3
6898,5
55790
0,35
9316,5
92970
0,4
12106
127860
0,45
16754,5
151250
0,50
23147,5
172.040
0,55
30710
195.000
0,60
39312
0
0,65
49062

Elke regel heeft 3 velden. Om belasting I te berekenen, zoeken we de eerste regel waar QF <= veld1. Als bijvoorbeeld QF = 23000 is, vinden we de regel

    24740        0.15        2072.5

De belasting I is dan gelijk aan 0,15*R - 2072,5*nbParts. Als QF zodanig is dat de relatie QF<=veld1 nooit geldt, dan worden de coëfficiënten van de laatste regel gebruikt. In dit geval:

    0                0.65        49062

wat resulteert in de belasting I = 0,65 * R - 49062 * nbParts.

De klasse „impot“ wordt als volgt gedefinieerd:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System

Public Class impot
    ' de gegevens die nodig zijn voor de berekening van de belasting
    ' zijn afkomstig van een externe bron
    Private limites(), coeffR(), coeffN() As Decimal

    ' fabrikant
    Public Sub New(ByVal LIMITES() As Decimal, ByVal COEFFR() As Decimal, ByVal COEFFN() As Decimal)
        ' er wordt gecontroleerd of de 3 tabellen dezelfde grootte hebben
        Dim OK As Boolean = LIMITES.Length = COEFFR.Length And LIMITES.Length = COEFFN.Length
        If Not OK Then
            Throw New Exception("Les 3 tableaux fournis n'ont pas la même taille(" & LIMITES.Length & "," & COEFFR.Length & "," & COEFFN.Length & ")")
        End If
        ' alles klopt
        Me.limites = LIMITES
        Me.coeffR = COEFFR
        Me.coeffN = COEFFN
    End Sub

    ' berekening van de belasting
    Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
        ' berekening van het aantal aandelen
        Dim nbParts As Decimal
        If marié Then
            nbParts = CDec(nbEnfants) / 2 + 2
        Else
            nbParts = CDec(nbEnfants) / 2 + 1
        End If
        If nbEnfants >= 3 Then
            nbParts += 0.5D
        End If
        ' berekening van het belastbaar inkomen en de gezinsquotiënt
        Dim revenu As Decimal = 0.72D * salaire
        Dim QF As Decimal = revenu / nbParts
        ' berekening van de belasting
        limites((limites.Length - 1)) = QF + 1
        Dim i As Integer = 0
        While QF > limites(i)
            i += 1
        End While
        Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
    End Function
End Class

Er wordt een belastingobject aangemaakt met de gegevens die nodig zijn om de belasting van een belastingplichtige te berekenen. Dit is het vaste deel van het object. Zodra dit object is aangemaakt, kan de methode **calculer** herhaaldelijk worden aangeroepen om de belasting van de belastingplichtige te berekenen op basis van zijn burgerlijke staat (gehuwd of ongehuwd), het aantal kinderen en zijn jaarsalaris. Een testprogramma zou er als volgt uit kunnen zien:


' opties
Option Strict On
Option Explicit On 

' naamruimten
Imports System
Imports Microsoft.VisualBasic

Module test
    Sub Main()
        ' interactief programma voor belastingberekening
        ' de gebruiker voert drie gegevens in via het toetsenbord: gehuwd nbEnfants salaris
        ' het programma geeft vervolgens de te betalen belasting weer
        Const syntaxe As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"

        ' tabellen met gegevens die nodig zijn voor de belastingberekening
        Dim limites() As Decimal = {12620D, 13190D, 15640D, 24740D, 31810D, 39970D, 48360D, 55790D, 92970D, 127860D, 151250D, 172040D, 195000D, 0D}
        Dim coeffR() As Decimal = {0D, 0.05D, 0.1D, 0.15D, 0.2D, 0.25D, 0.3D, 0.35D, 0.4D, 0.45D, 0.5D, 0.55D, 0.6D, 0.65D}
        Dim coeffN() As Decimal = {0D, 631D, 1290.5D, 2072.5D, 3309.5D, 4900D, 6898.5D, 9316.5D, 12106D, 16754.5D, 23147.5D, 30710D, 39312D, 49062D}

        ' aanmaken van een belastingobject
        Dim objImpôt As impot = Nothing
        Try
            objImpôt = New impot(limites, coeffR, coeffN)
        Catch ex As Exception
            Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
            Environment.Exit(1)
        End Try
        ' oneindige lus
        Dim marié As String
        Dim nbEnfants As Integer
        Dim salaire As Long
        While True
            ' er wordt gevraagd om de parameters voor de belastingberekening
            Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
            Dim paramètres As String = Console.In.ReadLine().Trim()
            ' moet er iets gebeuren?
            If paramètres Is Nothing OrElse paramètres = "" Then
                Exit While
            End If
            ' controle van het aantal argumenten in de ingevoerde regel
            Dim erreur As Boolean = False
            Dim args As String() = paramètres.Split(Nothing)
            Dim nbParamètres As Integer = args.Length
            If nbParamètres <> 3 Then
                Console.Error.WriteLine(syntaxe)
                erreur = True
            End If
            ' controle van de geldigheid van de parameters
            If Not erreur Then
                ' getrouwd
                marié = args(0).ToLower()
                If marié <> "o" And marié <> "n" Then
                    erreur = True
                End If
                ' nbEnfants
                Try
                    nbEnfants = Integer.Parse(args(1))
                    If nbEnfants < 0 Then
                        Throw New Exception
                    End If
                Catch
                    erreur = True
                End Try
                ' salaris
                Try
                    salaire = Integer.Parse(args(2))
                    If salaire < 0 Then
                        Throw New Exception
                    End If
                Catch
                    erreur = True
                End Try
            End If
            ' als de parameters correct zijn, wordt de belasting berekend
            If Not erreur Then
                Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire) & " F"))
            Else
                Console.Error.WriteLine(syntaxe)
            End If
        End While
    End Sub
End Module

Hier volgt een voorbeeld van de uitvoering van het vorige programma:

dir>dir
12/03/2004  18:20             1 483 impots.vb
12/03/2004  18:21             2 805 test.vb
dos>vbc /t:library impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dir>dir
12/03/2004  18:24             4 096 impots.dll
12/03/2004  18:20             1 483 impots.vb
12/03/2004  18:21             2 805 test.vb
dos>vbc /r:impots.dll test.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4 pour Microsoft (R) .NET Framework version 1.1.4322.573
dos>dir
12/03/2004  18:24             4 096 impots.dll
12/03/2004  18:20             1 483 impots.vb
12/03/2004  18:26             6 144 test.exe
12/03/2004  18:21             2 805 test.vb
dos>test
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :x x x
syntaxe : marié nbEnfants salaire
marié : o pour marié, n pour non marié
nbEnfants : nombre d'enfants
salaire : salaire annuel en F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 200000
impôt=22504 F
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :