arrow-left

All pages
gitbookPowered by GitBook
1 of 11

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Attributen

circle-check

Kenniscliparrow-up-right (let op: de demonstratie SchoolAdmin is verouderd en wordt nu anders aangepakt. De tekst heeft voorrang!)

Attributen, ook velden of instantievariabelen genoemd, zijn stukjes data die je bijhoudt in objecten. Ze stellen informatie voor die deel uitmaakt van een (object van een) klasse. Ze werken zoals de variabelen die je al kent, maar hun scope is een klasse of een object van een klasse, afhankelijk van de vraag of ze static zijn of niet. Door gebruik te maken van attributen, kunnen we stukjes data die samen horen ook samen houden op het niveau van de code. Alle data die samen hoort netjes groeperen en op een gestructureerd toegankelijk maken valt onder het begrip encapsulatie dat reeds eerder aan bod kwam.

circle-info

Attributen behoren tot een algemenere categorie onderdelen van objecten genaamd members.

hashtag
Basisvoorbeelden

Een typisch voorbeeld van een klasse is Auto. Er zijn verschillende stukjes data die deel kunnen uitmaken van één auto: de kilometerstand, het benzinepeil, de datum van het laatste onderhoud,...

Een reeds gekende manier om verwante informatie bij te houden is met behulp van "gesynchroniseerde" arrays, d.w.z. arrays die verwante data bijhouden op overeenkomstige posities. Onderstaand voorbeeld toont dit voor auto's:

Als we nu een methode willen uitvoeren die iets doet met alle informatie over een auto (bijvoorbeeld een onderhoudsrapport afprinten), moeten we drie waarden meegeven. Het is ordelijker deze zaken bij te houden in een object van klasse Auto als volgt:

Al deze velden zijn voorlopig public. Dat hoeft niet absoluut, maar het vergemakkelijkt de presentatie. Verdere opties volgen snel. Het is ook een afspraak om publieke velden met een hoofdletter te schrijven. Met deze voorstelling kunnen we dit nu doen:

Je ziet hier al enkele voordelen van encapsulatie:

  • je hoeft niet bij te houden hoe de informatie verspreid is over meerdere plaatsen

  • als we meer informatie over auto's (bv. het oliepeil) in het onderhoudsrapport steken, hoeven we onze calls van PrintOnderhoudsrapport niet aan te passen

    • een kenmerk van goede code is dat wijzigingen typisch geen grote wijzigingen vereisen

hashtag
Beginwaarden

Een veld krijgt normaal de defaultwaarde voor zijn type. hebben we reeds gezien. Het is mogelijk de beginwaarde aan te passen met de syntax voor een toekenning:

Nu hebben nieuwe auto's standaard 5 km op de teller staan, enzovoort. Merk op: deze waarde wordt voor elk nieuw object opnieuw berekend. Als je dus twee auto's aanmaakt in je programma, zullen zij beide een verschillende datum van het laatste onderhoud bevatten.

hashtag
static attributen

Iets dat static is, hoort niet bij de objecten, maar wel bij de hele klasse. Bijvoorbeeld: voor auto's worden de milieunormen na een paar jaar strenger. Er is een vaste set milieunormen en de te halen milieunorm wordt vastgelegd voor alle auto's. Daarom zouden we de milieunorm als volgt kunnen bijhouden:

circle-exclamation

Herhaal: static betekent niet "onveranderlijk" of "vast". Het betekent dat iets op niveau van de klasse werkt en niet op niveau van de objecten van die klasse.

hashtag
Opdracht: SchoolAdmin

Deze opdracht maak je tijdens de les.

hashtag
Doelstelling

We willen een programma maken dat ons helpt om in een school te beheren welke studenten ingeschreven zijn, welke cijfers behaald werden, enzovoort.

We doen dit in een apart project SchoolAdmin.

hashtag
H10_1 SchoolAdmin Student attributen

We maken een klasse student, met publieke attributen voor de naam, geboortedatum, het studentennummer en de gevolgde cursussen. We houden ook bij hoeveel studenten er zijn via een attribuut StudentCounterdat de beginwaarde 1 krijgt.

hashtag
Klassediagram

Schrijf in de Main de code om 2 objecten te maken:

  • said: Said Aziz, geboren 1 juni 2000 met studentennummer 1; Said volgt Programmeren en Databanken

  • mieke: Mieke Vermeulen, geboren 1 januari 1998 met studentennummer 2; Mieke volgt Communicatie

De studentennummers krijgen een waarde door een toekenning van de StudentCounterdat die je na elke toekenning met één verhoogt.

Defaultwaardenarrow-up-right
class Program {
    public static void Main() {
        int aantalAutos = 3;
        int[] kilometers = new int[aantalAutos];
        double[] benzine = new double[aantalAutos];
        DateTime[] onderhoud = new DateTime[aantalAutos];
        for (int i = 0; i < aantalAutos; i++) {
            Console.WriteLine($"Kilometerstand van auto {i+1}?");
            kilometers[i] = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine($"Benzinepeil van auto {i+1}?");
            benzine[i] = Convert.ToDouble(Console.ReadLine());
            Console.WriteLine($"Jaar recentste onderhoud auto {i+1}?");
            int jaar = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine($"Maand recentste onderhoud auto {i+1}?");
            int maand = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine($"Dag recentste onderhoud auto {i+1}?");
            int dag = Convert.ToInt32(Console.ReadLine());
            onderhoud[i] = new DateTime(jaar,maand,dag);
        }
        // later in de code
        for (int i = 0; i < aantalAutos; i++) {
            PrintOnderhoudsrapport(kilometers[i],benzine[i],onderhoud[i]);
        }
    }
}
class Auto {
    public int Kilometers;
    public double Benzine;
    public DateTime LaatsteOnderhoud;
}
class Program {
    public static void Main() {
        int aantalAutos = 3;
        Auto[] autos = new Auto[aantalAutos];
        for (int i = 0; i < aantalAutos; i++) {
            Auto nieuweAuto = new Auto();
            autos[i] = nieuweAuto;
            Console.WriteLine($"Kilometerstand van auto {i+1}?");
            nieuweAuto.Kilometers = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine($"Benzinepeil van auto {i+1}?");
            nieuweAuto.Benzine = Convert.ToDouble(Console.ReadLine());
            Console.WriteLine($"Jaar recentste onderhoud auto {i+1}?");
            int jaar = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine($"Maand recentste onderhoud auto {i+1}?");
            int maand = Convert.ToInt32(Console.ReadLine());
            Console.WriteLine($"Dag recentste onderhoud auto {i+1}?");
            int dag = Convert.ToInt32(Console.ReadLine());
            nieuweAuto.LaatsteOnderhoud = new DateTime(jaar,maand,dag);
        }
        // later in de code
        for (int i = 0; i < aantalAutos; i++) {
            PrintOnderhoudsrapport(autos[i]);
        }
    }
}
class Auto {
    public int Kilometers = 5; // in de fabriek vinden bv. een aantal testen plaats
    public double Benzine = 10; // nieuwe auto's moeten kunnen rijden
    public DateTime LaatsteOnderhoud = DateTime.Now;
}
enum MilieuNormen {
    Euro1, Euro2, Euro3, Euro4, Euro5, Euro6
}

class Auto {
    public static MilieuNormen HuidigeNorm;
    // rest van de code voor Auto
}

Enumeraties: nog een eigen datatype

hashtag
Enum datatypes

Met klassen kan je dus complexe concepten zoals een lening modelleren in code. Soms hoeft het niet zo complex, maar is het toch nuttig om wat meer structuur te voorzien dan met heel brede datatypes zoals int en string. Met een string kan je bijvoorbeeld om het even welke tekst voorstellen, dus oneindig veel mogelijkheden. Met een byte kan je een getal tussen 0 en 255 voorstellen. Met de andere getaltypes kan je een getal in een ander bereik voorstellen. Soms matchen de waarden die je kan voorstellen niet met de mogelijkheden in het probleemdomein.

Als je de mogelijkheden in het probleemdomein op voorhand kan vastleggen, is het vaak handig gebruik te maken van een enumeratie (enum). Een enumeratie is een opsomming van alle mogelijke waarden voor een variabele van een bepaald type.

hashtag
Een voorbeeld: weekdagen

Stel dat je een programma moet schrijven dat afhankelijk van de dag van de week iets anders moet doen. Zonder enums zou je dit kunnen schrijven op 2 zeer foutgevoelige manieren:

  1. Met een int die een getal van 1 tot en met 7 kan bevatten

  2. Met een string die de naam van de dag bevat

Beide hebben tekortkomingen, omdat ze niet goed genoeg overeenstemmen met de mogelijkheden in de werkelijkheid.

hashtag
Slechte oplossing 1: Met ints

De waarde van de dag staat in een variabele int dagKeuze. We bewaren er 1 in voor Maandag, 2 voor dinsdag, enzovoort.

Deze oplossing heeft 2 grote nadelen:

  • Wat als we per ongeluk dagKeuze een niet geldige waarde geven, zoals 9, 2000, -4, etc. ? We kunnen daar wel conditionele code voor voorzien, maar de compiler zal ons niet waarschuwen als we dat vergeten!

  • De code is niet erg leesbaar. dagKeuze==2? Was dat nu maandag, dinsdag of woensdag (want misschien beginnen we te tellen vanaf zondag, of misschien beginnen we te tellen vanaf 0). Je kan wel afspraken maken binnen je team, maar het is altijd mogelijk een afspraak te vergeten.

hashtag
Slechte oplossing 2: Met strings

De waarde van de dag bewaren we nu in een variabele string dagKeuze. We bewaren de dagen als "maandag", "dinsdag", etc.

De code wordt nu wel leesbaarder dan met 1, maar toch is ook hier 1 groot nadeel:

  • De code is veel foutgevoeliger voor typfouten. Wanneer je "Maandag" i.p.v. "maandag" bewaart dan zal de if al niet werken. Iedere schrijffout of variant zal falen.

hashtag
Enumeraties: het beste van beide werelden

Het keyword enum geeft aan dat we een nieuw datatype maken. Gelijkaardig aan wat we voor een klasse doen, dus. Wanneer we dit nieuwe type hebben gedefinieerd kunnen we dan ook variabelen van dit nieuwe type aanmaken (dat is waarom we spreken van een "datatype"). Anders dan bij een klasse beperken we tot alleen de opgesomde opties.

In C# zitten al veel enum-types ingebouwd. Denk maar aan ConsoleColor: wanneer je de kleur van het lettertype van de console wilt veranderen gebruiken we een enum-type. Er werd reeds gedefinieerd wat de toegelaten waarden zijn, bijvoorbeeld: Console.ForegroundColor = ConsoleColor.Red;

Achter de schermen worden waarden van een enum type nog steeds voorgesteld als getallen. Maar je gebruikt deze getallen niet rechtstreeks en dat verkleint de risico's.

hashtag
Zelf enum maken

Zelf een enum type gebruiken gebeurt in 2 stappen:

  1. Het type en de mogelijke waarden definiëren

  2. Variabele(n) van het nieuwe type aanmaken en gebruiken in je code

Stap 1: het type definiëren

We maken eerst een enum type aan. Wij zullen dit doen in een aparte file met dezelfde naam als het nieuwe datatype. Bijvoorbeeld:

Vanaf nu kan je variabelen van het type Weekdagen aanmaken. Merk op dat er geen puntkomma voorkomt. De syntax is ongeveer dezelfde als die voor de definitie van een klasse.

Stap 2: variabelen van het type aanmaken en gebruiken.

We kunnen nu variabelen van het type Weekdagen aanmaken. Bijvoorbeeld:

En vervolgens kunnen we waarden aan deze variabelen toewijzen als volgt:

Kortom: we hebben variabelen zoals we gewoon zijn, het enige verschil is dat we nu beperkt zijn in de waarden die we kunnen toewijzen. Deze kunnen enkel de waarden zijn die in het type gedefinieerd werden. De code is nu ook een pak leesbaarder geworden.

hashtag
Waarden van een enum type inlezen

Eens je met enums aan het werken bent in je programma, kan je gerust zijn dat deze een geldige waarde bevatten. Er is alleen het probleem dat je soms een van de mogelijkheden moet krijgen van de gebruiker. Een waarde van een enum type rechtstreeks intypen gaat niet. Net zoals wanneer je een getal inleest, is er een omzetting nodig. Het verschil is dat we hier een dubbele omzetting doen: van tekst naar getal en dan van getal naar enum waarde.

Je kan dit als volgt doen:

Dit werkt en je hoeft de achterliggende getallen voor de waarden niet te kennen. Er zijn meer geavanceerde mogelijkheden, maar dit volstaat op dit punt.

if(dagKeuze==1)
{
    Console.WriteLine("We doen de maandag dingen");
}
else 
if (dagKeuze==2)
{
    Console.WritLine("We doen de dinsdag dingen");
}
else 
if //enz..
if(dagKeuze=="maandag")
{
    Console.WriteLine("We doen de maandag dingen");
}
else 
if (dagKeuze=="dinsdag")
{
    Console.WritLine("We doen de dinsdag dingen");
}
else 
if //enz..
namespace Programmeren {
    enum Weekdagen {Maandag, Dinsdag, Woensdag, Donderdag, Vrijdag, Zaterdag, Zondag}
}
Weekdagen dagKeuze;
Weekdagen andereKeuze;
dagKeuze = Weekdagen.Donderdag;
Weekdagen keuze;
Console.WriteLine("Welke dag is het vandaag?");
Console.WriteLine($"{(int) Weekdagen.Maandag}. {Weekdagen.Maandag}");
Console.WriteLine($"{(int) Weekdagen.Dinsdag}. {Weekdagen.Dinsdag}");
// enzovoort
keuze = (Weekdagen) Convert.ToInt32(Console.ReadLine());

OOP Intro

circle-check

Kenniscliparrow-up-right

hashtag
OOP

Object Oriented Programming of korter OOP is een techniek afkomstig van higher level programmeertalen zoals Java, C#, VB.NET, ... en vindt zijn oorsprong bij de programmeertaal Smalltalk, die het eerst de term Object Oriented Programming introduceerde.

In recentere jaren heeft deze techniek echter ook zijn weg gevonden naar scripting talen zoals Python, Ruby, Perl en zelfs PHP.

OOP streeft ernaar om een project zo structureel mogelijk op te bouwen in objecten. Dit heeft voor de programmeur het grote voordeel dat code vanaf nu in logische componenten wordt opgedeeld en veel makkelijker te hergebruiken is.

Om het concept van objecten te illustreren wordt vaak een voorwerp uit het dagelijks leven als voorbeeld gebruikt. Neem bijvoorbeeld een auto. De auto is het object en dit object heeft zowel geassocieerde data als functionaliteit. Deze data stellen eigenschappen of onderdelen van het object voor. Een eigenschap van de auto kan de kleur, de lengte, het aantal kilometers op de teller, of zelfs zijn huidige locatie of snelheid zijn. Deze zaken worden geïmplementeerd met ofwel instantievariabelen, ofwel properties. De functionaliteit is iets dat het object kan doen. Deze wordt geïmplementeerd met instantiemethodes. Als je een auto als object ziet, kunnen deze bijvoorbeeld starten, versnellen of remmen zijn.

Auto's zijn een veelgebruikt voorbeeld, maar eigenlijk schrijf je er zelden objecten voor tenzij je een soort simulator wil maken. Objecten hoeven niet altijd tastbare zaken te zijn. Je kan bijvoorbeeld ook een lening bij de bank voorstellen als een object. Dit object heeft dan onder andere volgende eigenschappen:

  • een begunstigde, d.w.z. de persoon die de lening heeft aangegaan

  • een restsaldo, d.w.z. het bedrag dat je moet betalen

  • een looptijd, d.w.z. de periode waarbinnen de lening afbetaald moet zijn

Maar met een lening kunnen we ook bepaalde zaken doen die we wel in een computersysteem zouden implementeren. Deze zaken vormen de functionaliteit, dus de verzameling instantiemethodes, van het lening-object:

  • een afbetaling verwerken

  • de uiteindelijk af te betalen som berekenen

  • het afbetaald kapitaal en de afbetaalde interest na een bepaald aantal maanden berekenen

Dus voor zaken die (minstens) zo uiteenlopend zijn als een auto en een lening hebben we een combinatie van data en functionaliteit. Zo zit een object in een programmeertaal er ook uit.

hashtag
Black-box principe

Een belangrijk concept bij OOP is het black-box principe waarbij we de afzonderlijke objecten en hun werking als "zwarte dozen" gaan beschouwen. Dat wil zeggen dat we (meestal) niet gaan kijken hoe ze in elkaar zitten. Neem het voorbeeld van de auto: deze is in de echte wereld ontwikkeld volgens het blackbox-principe. De werking van de auto kennen tot in het kleinste detail is niet nodig om met een auto te kunnen rijden. De auto biedt een aantal zaken aan de buitenwereld aan (het stuur, pedalen, het dashboard), wat we de publieke interface of gewoonweg interface noemen. Deze zaken kan je gebruiken om de interne staat van de auto uit te lezen of te manipuleren. Stel je voor dat je moest weten hoe een auto volledig werkte voor je ermee op de baan kon.

Binnen OOP wordt dit blackbox-concept encapsulatie genoemd. Het doel van OOP is andere programmeurs (en jezelf) zoveel mogelijk af te schermen van de interne werking van je code. Net als bij een auto, moet je alleen weten wat elk stukje van de interface klaarspeelt, maar hoef je niet te weten hoe dit allemaal is geprogrammeerd.

circle-info

Encapsulatie wordt vaak een van de vier pijlers van objectgeoriënteerd programmeren genoemd. De andere zijn abstractie, overerving en polymorfisme. Deze komen allemaal later in de cursus aan bod.

hashtag
Klassen en objecten

Een elementair aspect binnen OOP is het verschil beheersen tussen een klasse en een object.

Wanneer we meerdere objecten gebruiken van dezelfde soort dan kunnen we zeggen dat deze objecten allemaal deel uitmaken van een zelfde klasse. Zo hebben we bijvoorbeeld de klasse van de leningen. Er zijn leningen van €50.000, er zijn leningen van €10.000.000. Er zijn er met een intrestvoet van 0.2% en er zijn er met een intrestvoet van 2%. Er zijn er met een looptijd van 5 jaar en er zijn er met een looptijd van 20 jaar. Het zijn allemaal leningen, maar ze hebben allemaal andere eigenschappen.

circle-info

Als je vertrouwd bent met relationele databanken: een klasse is vergelijkbaar met een tabel of entiteittype, terwijl een object vergelijkbaar is met een record of een entiteit.

Objecten van dezelfde soort volgen wel dezelfde regels en hebben in dat opzicht dezelfde functionaliteit. Maar de eigenschappen van object bepalen ook het resultaat van de instantiemethodes. Eerder zagen we dat de uiteindelijk af te betalen som berekend kan worden door een lening. Deze som zal per definitie hoger zijn van een lening met een hoge rentevoet dan voor een lening met een lage rentevoet (als de andere eigenschappen gelijk zijn).

Samengevat:

  • Een klasse is een beschrijving en verzameling van dingen (objecten) met soortgelijke eigenschappen en gedrag.

  • Een individueel object is een instantie (exemplaar, voorbeeld, verschijningsvorm) van een klasse

een intrestvoet, die uiteindelijk bepaalt hoe veel je bovenop het oorspronkelijke bedrag betaalt

H10: Klassen en objecten

Klassen en objecten aanmaken

In C# kunnen we geen objecten aanmaken voor we een klasse hebben gedefinieerd die de algemene eigenschappen (properties) en werking (methoden) beschrijft.

hashtag
Klasse maken

Een heel elementaire klasse heeft de volgende vorm:

We maken hier normaal gebruik van Pascal case. Je kan ook nog enkele toevoegingen doen die niet zuiver data en functionaliteit zijn, maar die houden we voor verder in de cursus.

Volgende code beschrijft de klasse Auto in C#

Binnen het codeblock dat bij deze klasse hoort zullen we verderop dan de werking beschrijven.

hashtag
Klassen in Visual Studio

Je kan "eender waar" een klasse aanmaken, maar het is een goede gewoonte om per klasse een apart bestand te gebruiken. Dit is ook de afspraak die wij zullen volgen.

  • In de solution explorer, rechterklik op je project

  • Kies Add

  • Kies Class...

circle-exclamation

De naam van je klasse moet voldoen aan de identifier regels die ook gelden voor het aanmaken van variabelen!

hashtag
Objecten aanmaken

Je kan nu objecten aanmaken van de klasse die je hebt gedefinieerd. De klasse is een type en je kan dus ook variabelen aanmaken die bedoeld zijn om objecten van deze klasse in bij te houden. Je doet dit door eerst een variabele te definiëren met als type de naam van de klasse en vervolgens een object te instantiëren met behulp van het new keyword:

We hebben nu twee objecten aangemaakt van het type Auto.

Let goed op dat je dus op de juiste plekken dit alles doet (bekijk de onderstaande screenshot):

  • Klassen maak je aan als aparte files in je project

  • Objecten creëer je in je code op de plekken dat je deze nodig hebt, bijvoorbeeld in je Main methode bij een Console-applicatie

circle-info

Je hebt dus in het verleden ook al objecten aangemaakt met new. Telkens je met Random werkt deed je dit al. Dit wil zeggen dat er dus in .NET ergens reeds een voorgeprogrammeerde klasse Random bestaat met de interne werking.

class ClassName
{
    // hier komen de data en functionaliteit
}
Geef een goede naam voor je klasse
Klasse toevoegen in VS
basics oop same in vv
class Auto
{
    // data kan bv. nummerplaat, bouwjaar of kilometerstand zijn
    // gedrag kan bv. bepalen van de verkoopwaarde zijn
}
Auto mijnEerste = new Auto();
Auto mijnAndereAuto = new Auto();

Klassen en objecten weergeven deel 1

circle-check

Kenniscliparrow-up-right

hashtag
Klassen voorstellen

De data en functionaliteit van een klasse, en ook haar relatie tot andere klassen, wordt vaak voorgesteld in een UML-klassendiagram. Hoe je verschillende klassen met elkaar verbindt, houden we voor iets verderop. Ook enkele andere details volgen later. Wat we wel al kunnen vertellen:

  • elke klasse wordt voorgesteld als een rechthoek, met bovenaan in die rechthoek de naam van de klasse

  • in een tweede vakje worden dan de eigenschappen gegeven, gewoonlijk met hun datatype

  • in een derde vakje worden dan de methoden gegeven, met hun parameters en hun returntype

circle-info

Het (return) type kan voor de naam van een attribuut of methode staan (zoals in C#), of het kan helemaal achteraan staan, voorafgegaan door een dubbele punt (zoals in TypeScript).

hashtag
Voorbeeld 1: Lening

Dit vertelt ons dat een object van klasse Lening beschikt over een saldo en een intrestvoet en het vertelt ons ook welke datatypes we gebruiken om deze voor te stellen. Bovendien kan een Lening zelf bepalen hoe veel we in totaal zullen moeten betalen als we elke maand een vast bedrag aflossen. Dit is functionaliteit van het object, die we zullen implementeren met een instantiemethode.

hashtag
Voorbeeld 2: Auto

Dit vertelt ons dat elke Auto een eigen kilometerstand, bouwjaar en huidige snelheid heeft. Het is mogelijk de Auto te laten starten en om hem te laten versnellen met een geven aantal kilometer per uur.

hashtag
Objecten voorstellen

Een UML-klassendiagram dient voor... klassen. Als je wil kijken naar objecten, gebruik je een sequentiediagram. We gaan hier nog niet erg diep op in, maar we maken alvast duidelijk dat je op een klassendiagram geen objecten ziet.

Een voorbeeld van een sequentiediagram voor een programma dat twee leningen aanmaakt (bijvoorbeeld een programma in een bank om een simulatie te maken voor een klant):

Het figuurtje met als naam ExterneCode stelt iets voor dat ons eigenlijk niet interesseert. Wat de pijlen en de berichten daarop technisch betekenen, maakt voorlopig ook niet zo uit. Het belangrijkste hier is dat er twee objecten van de klasse Lening zijn. Dit wordt extra in de verf gezet omdat ze twee verschillende namen hebben (lening1 en lening2), maar allebei hetzelfde type na de dubbele punt (Lening). Op een klassediagram ga je nooit twee keer een box voor dezelfde klasse zien.

De "C" bovenaan staat voor "class". De meeste programma's tekenen deze niet.

Oefeningen

hashtag
Richtlijnen

hashtag
Structuur oefeningen

Vanaf hier veronderstellen we dat je in één groot project (OOExercises) werkt dat één klasse Program heeft. Deze klasse heeft een Main methode die een keuzemenu opstart. Oefeningen rond eenzelfde topic worden (statische) methodes van één klasse met een methode ShowSubmenu, die je een menu toont van alle oefeningen over dat topic en die je toestaat een oefening naar keuze te testen. Dit wordt uitgelegd in de eerste oefening.

hashtag
Oefening: H10-voorbereiding

hashtag
Leerdoelen

  • een ordelijke menustructuur voor je code voorzien

hashtag
Functionele analyse

We willen dat we alle oefeningen die we in dit vak maken op een ordelijke manier kunnen opstarten. We doen dit door een keuzemenu met twee niveaus te voorzien: de gebruiker kan elke reeds geschreven oefening uitvoeren door eerst het algemene onderwerp aan te geven en vervolgens de specifieke oefening.

hashtag
Technische analyse

  • Maak een project OOExercises.

  • Laat in je Main methode een lijst van alle topics zien waarover oefeningen gemaakt zijn. In het begin is dit enkel DateTime. De gebruiker kan een topic aanduiden door een nummer in te geven, want voor elk topic staat ook een nummer.

hashtag
Voorbeeldinteractie

circle-exclamation

Dit is maar een voorbeeld! De getoonde topics en oefeningen gaan afhangen van wat je al gedaan hebt.

hashtag
Oefening: H10-Clock

Maak een applicatie die bestaat uit een oneindige loop. De loop zal iedere seconde pauzeren: System.Threading.Thread.Sleep(1000);

Vervolgens wordt het scherm leeg gemaakt en wordt de huidige tijd getoond. Merk op dat ENKEL de tijd wordt getoond, niet de datum.

hashtag
Oefening: H10-Birthday

Maak een applicatie die aan de gebruiker vraagt op welke dag hij/zij jarig is. Toon vervolgens over hoeveel dagen de verjaardag van de gebruiker zal zijn.

hashtag
Oefening: H10-DayOfTheWeek

hashtag
Leerdoelen

  • aanmaken van DateTime objecten

  • formatteren van DateTime objecten

hashtag
Functionele analyse

We willen voor een willekeurige datum kunnen bepalen welke dag van de week het is.

hashtag
Technische analyse

  • je moet eerst de dag, maand en jaar opvragen en een DateTime aanmaken

  • daarna moet je laten zien over welke dag van de week (in het Nederlands) het gaat

    • gebruik hiervoor formattering van een DateTime

hashtag
Voorbeeldinteractie

hashtag
Oefening: H10-TicksSince2000

hashtag
Leerdoelen

  • aanmaken van DateTime objecten

hashtag
Functionele analyse

We willen weten hoe veel fracties van een seconde al verlopen zijn sinds het begin van de jaren 2000.

hashtag
Technische analyse

  • .NET stelt deze fracties (1 / 10000 milliseconden) voor als "ticks"

  • We willen weten hoe veel ticks er voorbijgegaan zijn sinds het absolute begin van het jaar 2000

  • Noem de methode waarin je dit schrijft TicksSince2000

hashtag
Voorbeeldinteractie

hashtag
Oefening: H10-LeapYearCount

hashtag
Leerdoelen

  • gebruik van een statische methode

hashtag
Functionele analyse

We willen bepalen hoe veel schrikkeljaren er zijn tussen 1799 en 2021.

hashtag
Technische analyse

  • implementeer zelf geen logica voor schrikkeljaren, maar laat dit over aan de klassen DateTime

  • maak gebruik van een statische methode van deze klasse

  • noem je methode LeapYearCount

hashtag
Voorbeeldinteractie

hashtag
Oefening: H10-CodeTiming

hashtag
Leerdoelen

  • eenvoudig code leren timen

  • gebruiken van DateTime

  • herhaling arrays

hashtag
Functionele analyse

We zijn benieuwd hoe lang het duurt een array van 1 miljoen ints te maken en op te vullen met de waarden 1,2,...

hashtag
Technische analyse

  • Bepaal het tijdstip voor en na aanmaken van de array.

  • Vul de array in met een for-lus.

  • Noem de methode waarin je dit schrijft CodeTiming

hashtag
Voorbeeldinteractie

hashtag
Oefeningen na Access modifiers:

hashtag
Oefening: H10-CombinationOf2Numbers

hashtag
Leerdoelen

  • werken met klassen en objecten

  • instantieattributen

  • instantiemethoden

hashtag
Functionele analyse

Dit programma geeft op basis van de input van twee getallen de som van beide getallen, het verschil, het product en de deling. In het laatste geval en indien er een deling door nul zou worden uitgevoerd, wordt er "Fout!" weergegeven.

hashtag
Technische analyse

Voorzie voor volgende oefening eerst een nieuwe submenuklasse met als naam ClassesAndObjects.

Maak ook een eigen klasse CombinationOf2Numbers in een eigen file, CombinationOf2Numbers.cs. Deze klasse bevat 2 getallen (type int). Er zijn 4 methoden, die allemaal een double teruggeven:

  • Sum: geeft som van beide getallen weer

  • Difference: geeft verschil van beide getallen weer

  • Product

Gebruik public attributen Number1en Number2. Plaats onderstaande code in een publieke statische methode van ClassesAndObjectsmet naam DemoCombinationOf2Numbersmet return type void:

Zorg dat je DemoCombinationOf2Numberskan oproepen via het submenu van ClassesAndObjects.

hashtag
Voorbeeldinteractie(s)

hashtag
H10_4 SchoolAdmin klasse Course

We willen per cursus bijhouden welke studenten ingeschreven zijn en maken daarvoor een klasse Course.

hashtag
Klassediagram

Deze klasse heeft twee attributen: Students en Title. Students is een List van Student-objecten. Title is gewoonweg een string. Courseheeft ook een methode ShowOverviewdie de titel van de cursus toont, gevolgd door de namen van alle studenten die de cursus volgen.

Test je programma door een statische methode te maken in Program.cs DemoCourses. Deze methode maakt eerst twee studenten aan (dezelfde als in DemoStudents; je kan de code kopiëren). Maak 4 cursussen aan ("Communicatie", "Databanken", "Programmeren" en "Webtechnologie") via variabelen communicatie, programmeren, webtechnologie en databanken en geef ze de juiste titel. Maak de 2 studenten lid van de cursussen zoals hieronder getoond wordt. Toon tenslotte voor elke cursus het overzicht via ShowOverview. De methode DemoCourses wordt ook opgeroepen via het keuzemenu in Main. Plaats deze code in een oneindige lus.

hashtag
Voorbeeldinteractie

hashtag
H10_5 SchoolAdmin klasse CourseResult

We willen voor de student goed kunnen bijhouden voor welke cursus er welk resultaat behaald werd. Daarvoor maken we een nieuwe klasse CourseResult. Daarna passen we ook de klasse Student aan.

hashtag
Klassediagram

Deze klasse bevat twee attributen: Name van het type string en Result van het type byte.

Er zijn een aantal wijzigingen in de klasse Student.

hashtag
Klassediagram

Om deze klasse te gebruiken in klasse Student, vervangen we de list coursesdoor een list courseResultsvan CourseResult-objecten en vervangen we de methode RegisterForCourse door RegisterCourseResult.

RegisterCourseResultcontroleert eerst het resultaat. Indien dit groter is dan 20, geef dan de melding "Ongeldig cijfer!". Indien het een geldig resultaat is:

  • maak je een nieuw object van de klasse CourseResult;

  • geef je het attribuut Name vandit object de waarde van de parameter course;

  • geef je het attribuut Result

Doe de nodige aanpassing aan de methode DetermineWorkload.

DemoStudents en DemoCourses moeten ook aangepast worden:

  • Verwijder de lijnen met gebruik van RegisterForCourse .

  • We schrijven elke student in voor enkele cursussen met behulp van RegisterCourseResulten we kennen ook resultaten toe: Said: Communicatie: 12; Programmeren: 15; Webtechnologie: 13 Mieke: Communicatie: 13; Programmeren: 16; Databanken:14

De uitvoer van deze 2 methodes is niet gewijzigd.

hashtag
H10_6 SchoolAdmin Student Average

Voor een student gaan we een gemiddelde berekenen van de behaalde resultaten en we tonen dit in een overzicht.

hashtag
Klassediagram

Maak de methode Average. Deze overloopt de list courseResults, maakt het totaal van alle resultaten en deelt dit door het aantal resultaten.

De methode ShowOverviewtoont eerst de naam van de student met daaronder de werkbelasting en vervolgens een cijferrapport met de gevolgde cursussen en de behaalde resultaten. Onderaan staat het gemiddelde.

Pas de methode DemoStudents aan (in Program.cs). Schrap hier de methoden GenerateNameCarden DetermineWorkloaden voer voor elke student de methode ShowOverviewuit.

Resultaat van DemoStudents:

hashtag
Oefeningen na Properties:

hashtag
Oefening: H10-Figures

hashtag
Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

hashtag
Functionele analyse

Dit programma maakt enkele rechthoeken en driehoeken met gegeven afmetingen (in meter) aan, berekent hun oppervlakte en toont deze info aan de gebruiker. De rechthoeken en driehoeken die worden aangemaakt, zijn al gecodeerd in het programma. De gebruiker hoeft dus niets anders te doen dan het programma te starten.

hashtag
Technische analyse

Er is een klasse Rectanglemet full properties Width en Heighten een klasse Trianglemet Base en Height. Je programma maakt de figuren die hierboven beschreven worden aan met beginwaarde 1.0 voor elke afmeting en stelt daarna hun afmetingen in via de setters voor deze properties. De oppervlakte wordt bepaald in een read-only property (dus met alleen een getter en geen setter). Deze heet Areaen is van het type double.

circle-info

base is een keyword in C#; je kan als achterliggend privaat veld @base gebruiken.

Indien om het even welk van deze properties wordt ingesteld op 0 of minder, signaleer je dit via de code Console.WriteLine($"Het is verboden een (afmeting) van (waarde) in te stellen!") (zie voorbeeldcode).

circle-info

De wiskundige formule voor de oppervlakte van een driehoek is basis * hoogte / 2.

Schrijf de voorbeelden uit in een static methode DemoFigures van de klasse ClassesAndObjects.

hashtag
Voorbeeldinteractie(s)

(Er worden twee rechthoeken en twee driehoeken aangemaakt. De afmetingen van de eerste rechthoek worden eerst op -1 en 0 ingesteld. Daarna krijgen ze de waarden die je ziet in het bericht hieronder. Formatteer ook met 1 cijfer na de komma.)

hashtag
H10_7 SchoolAdmin properties Course

We willen elke cursus automatisch een id toewijzen en ook willen we studiepunten per cursus bijhouden. Daarnaast willen we een lijst bijhouden met alle objecten van de klasse Cursus.

hashtag
Klassediagram

Maak de property CreditPointsmet een private setter.

Voeg de read-only property Id toe. Om telkens een volgend nummer toe te wijzen aan dit id, maak je het static attribuut maxId dat je de beginwaarde 1 geeft.

Maak het attribuut AllCourses(static); dit is een list van Course-objecten.

hashtag
H10_8 SchoolAdmin properties CourseResult

We herschrijven de bestaande attributen naar properties

hashtag
Klassediagram (er is geen verschil te zien)

Van Name maak je een property Name.

Van Result maak je een property Result. Zorg ervoor dat er enkel een resultaat kan ingesteld worden dat niet meer is dan 20.

hashtag
H10_9 SchoolAdmin Student Age

Op basis van de geboortedatum berekenen we de exacte leeftijd van de student en nemen we die mee op in het overzicht.

hashtag
Klassediagram

Maak de read-only property Agezonder achterliggend veld ('computed property'). Bereken hier eerst het verschil in jaren tussen het huidige jaar en het geboortejaar. Pas daarna dit aantal jaren eventueel aan (-1) als de student nog niet verjaard is (de maand van de verjaardag is na de huidige maand; of we zijn de maand van de verjaardag maar de verjaardag is na de huidige dag).

Voeg de leeftijd toe aan het overzicht in ShowOverview. Voorbeeld:

hashtag

Gebruik een switch op de gebruikersinput om te bepalen van welk topic de
ShowSubmenu
methode moet worden opgeroepen. Deze methode heeft return type
void
en geen parameters.
  • Voorzie een eerste klasse, DateTimeExercises, met deze methode ShowSubmenu. Totdat je oefeningen hebt om te demonstreren, toont ShowSubmenu gewoonweg de tekst "Er zijn nog geen oefeningen over dit topic".

  • Indien er wel oefeningen zijn (deze oefening moet je dus updaten naarmate je vordert), wordt elke reeds geprogrammeerde oefening genummerd en getoond en kan de gebruiker kiezen om deze uit te voeren.

  • Nadat een oefening getest is, kan je opnieuw een topic en een oefening kiezen. Het programma eindigt nooit.

  • laat ook de datum zelf zien in een formaat dat leesbaar is voor de gebruiker

  • noem de methode waarin je dit schrijft DayOfTheWeek .

  • : geeft product van beide getallen weer
  • Quotient: geeft deling van beide getallen weer. Print "Fout" naar de console indien je zou moeten delen door 0 en voer dan de deling uit. Wat er dan gebeurt, is niet belangrijk.

  • van dit object de waarde van de parameter result;
  • voeg je het nieuwe object toe aan de lijst courseResults.

  • Welkom bij de oefeningen van Objectgeoriënteerd Programmeren!
    Topic van de uit te voeren oefening?
    1. DateTime
    2. Properties en access modifiers
    > 1
    Uit te voeren oefening?
    1. H10-Clock
    2. H10-Birthday
    > 2
    (...)
    Welke dag?
    > 14
    Welke maand?
    > 2
    Welk jaar?
    > 2020
    14 februari 2020 is een vrijdag.
    Sinds 1 januari 2000 zijn er (hier wordt het aantal getoond) ticks voorbijgegaan.
    Er zijn (hier wordt het aantal getoond) schrikkeljaren tussen 1799 en 2021.
    Het duurt (hier wordt het aantal getoond) milliseconden om een array van een miljoen elementen aan te maken en op te vullen met opeenvolgende waarden.
    CombinationOf2Numbers pair = new CombinationOf2Numbers();
    pair.Number1= 12;
    pair.Number2= 34;
    Console.WriteLine("Paar:" + pair.Number1+ ", " + pair.Number2);
    Console.WriteLine("Som = " + pair.Sum());
    Console.WriteLine("Verschil = " + pair.Difference());
    Console.WriteLine("Product = " + pair.Product());
    Console.WriteLine("Quotient = " + pair.Quotient());
    Paar: 12, 34
    Som = 46
    Verschil = -22
    Product = 408
    Quotient = 0,352941176470588
    Het is verboden een breedte van -1 in te stellen!
    Het is verboden een breedte van 0 in te stellen!
    Een rechthoek met een breedte van 2,2m en een hoogte van 1,5m heeft een oppervlakte van 3,3m².
    Een rechthoek met een breedte van 3m en een hoogte van 1m heeft een oppervlakte van 3m².
    Een driehoek met een basis van 3m en een hoogte van 1m heeft een oppervlakte van 1,5m².
    Een driehoek met een basis van 2m en een hoogte van 2m heeft een oppervlakte van 2m².

    Methoden

    circle-check

    Kenniscliparrow-up-right

    Instantiemethoden, ook objectmethoden genoemd, weerspiegelen staan toe om functionaliteit toe te voegen aan objecten van een bepaalde klasse. Soms wordt ook gezegd dat ze "gedrag" van de objecten voorzien. Ze verschillen van statische methoden omdat ze niet alleen gebruik kunnen maken van statische onderdelen van klassen, maar ook van het object waar ze zelf bij horen.

    circle-info

    Methoden behoren tot een algemenere categorie onderdelen van objecten genaamd members.

    hashtag
    Basisvoorbeelden

    We gaan verder met de klasse Auto. We willen bijvoorbeeld een applicatie voor de opvolging van deelauto's (Cambio, Poppy, etc.) schrijven. Er zijn verschillende soorten functionaliteit die je kan koppelen aan één auto:

    • voltanken

    • rijden

    • op onderhoud gaan

    circle-info

    Is het de auto die deze zaken doet, of is het een persoon? In werkelijkheid is het natuurlijk dat laatste. Maar de functionaliteit is wel veel sterker gelinkt aan auto's dan aan personen en misschien interesseert de persoon die de handeling uitvoert ons niet eens.

    Je doet dit met objectmethoden. Deze lijken erg op static methoden, maar ze hebben toegang tot het object waarop ze zijn toegepast.

    Een simpele implementatie van dit gedrag zie je hier:

    circle-exclamation

    Bovenstaande code is kort om didactische redenen. Er wordt niet gecontroleerd dat je benzinepeil altijd minstens 0l is, er wordt verondersteld dat de capaciteit van je tank 50l is,...

    circle-info

    Voor de duidelijkheid kan je het woordje this toevoegen om het huidige object expliciet voor te stellen. Het wordt sterk aangeraden dat je dit doet. Je code wordt er beter leesbaar door.

    hashtag
    Gebruik

    Om een objectmethode te gebruiken, hebben we een object nodig. We schrijven dan de naam van het object, gevolgd door een punt en een methodeoproep.

    Het gedrag van een object kan afhangen van de waarde van de instantievariabelen. Zo zal de verkoopswaarde van auto1 iets lager liggen dan die van auto2. Dat komt omdat this.Kilometers deel uitmaakt van de berekening van de verkoopsprijs. Ook dit valt onder het principe van encapsulatie: er vindt een berekening plaats "onder de motorkap". We hoeven niet te weten hoe de prijs berekend wordt, elk object weet van zichzelf hoe het de prijs berekent.

    hashtag
    static methodes

    Een statische methode is een methode die wel bij de klasse hoort, maar niet te maken heeft met een specifiek object van die klasse. We gebruiken terug de euronorm als voorbeeld:

    hashtag
    H10_2 SchoolAdmin Student methoden

    We breiden onze klasse uit met 2 methoden.

    hashtag
    Klassediagram

    GenerateNameCardtoont de naam van de student gevolgd door (STUDENT).

    DetermineWorkloadberekent de werkbelasting van de student aan de hand van het aantal cursussen waarvoor zij/hij is ingeschreven. Er wordt gerekend met 10u per week per cursus.

    Maak in de Main een keuzemenu zoals hieronder.

    hashtag
    Voorbeeldinteractie

    Optie 1 roept een methode DemoStudents op. Maak deze in Program.cs. Verplaats daarvoor de reeds bestaande code in de Main naar de nieuwe methode en vul ze aan met de oproepen naar de juiste methodes om bovenstaand resultaat te krijgen.

    verkoopsprijs bepalen
    class Auto {
    
        // objectvariabelen van eerder zijn er nog
    
        public void Voltanken()
        {
            Benzine = 50.0; // we veronderstellen even dat dat het maximum is
        }
    
        public void Rijden(int aantalKilometers)
        {
            Kilometers += aantalKilometers;
            Benzine -= 5.0 * (aantalKilometers/100.0);
        }
    
        public void Onderhouden()
        {
            LaatsteOnderhoud = DateTime.Now;
        }
    
        public double VerkoopsprijsBepalen()
        {
            return Math.Max(10000 * (1 - Kilometers / 200000.0),1000);
        }
    }
    // in Program.cs
    public static void DemonstreerAttributen() {
        Auto auto1 = new Auto();
        Auto auto2 = new Auto();
        auto1.Voltanken();
        auto1.Rijden(5);
        auto1.Rijden(10);
        auto1.Rijden(20);
        Console.WriteLine(auto1.Kilometers);
        Console.WriteLine(auto2.Kilometers);
    }
    enum MilieuNormen {
        Euro1, Euro2, Euro3
    }
    
    class Auto {
        public static MilieuNormen HuidigeNorm;
        // rest van de code voor Auto
        public static void VerklaarNorm(MilieuNormen norm) {
            int jaartal;
            double CO;
            switch (norm) {
                case MilieuNormen.Euro1:
                    jaartal = 1992;
                    CO = 1.0;
                    break;
                case MilieuNormen.Euro2:
                    jaartal = 1996;
                    CO = 1.0;
                    break;
                case MilieuNormen.Euro3:
                    jaartal = 2000;
                    CO = 0.64;
                    break;
                default:
                    jaartal = -1;
                    CO = -1;
                    break;
            }
            Console.WriteLine($"Geïntroduceerd in {jaartal}");
            Console.WriteLine($"{CO} gram CO per km");
        }
    }

    Access modifiers

    circle-check

    Kenniscliparrow-up-right

    Access modifiers bepalen welke code door welke andere code mag worden uitgevoerd of aangepast. We hebben al een aantal dingen public gemaakt bij wijze van demonstratie. Dit is handig om voorbeeldjes te tonen, maar in code van hoge kwaliteit, denk je grondig na voor je dit doet.

    hashtag
    Public en private access modifiers

    De access modifier geeft aan hoe zichtbaar een bepaald deel van de klasse is voor code buiten de klasse zelf. Wanneer je niet wilt dat "van buitenuit" (bv in Main, terwijl je een andere klasse dan Program schrijft) een bepaalde methode kan aangeroepen worden, dan dien je deze als private in te stellen. Wil je dit net wel dat moet je er expliciet public voor zetten.

    Test in de voorgaande klasse Auto eens wat gebeurt wanneer je public verwijdert voor een van de methodes. Inderdaad, je krijgt een foutmelding. Lees deze. Ze zegt dat de methode die je wil oproepen wel bestaat, maar niet gebruikt mag worden. Dat komt omdat je testcode in de klasse Program staat en je methode deel uitmaakt van een andere klasse (meerbepaald Auto).

    Als je duidelijk wil maken dat bepaalde code niet van buitenaf aangeroepen kan worden, schrijf dan private in plaats van public. Er zijn nog tussenliggende niveaus waar we later op ingaan en als je geen van beide modifiers schrijft, kan het zijn dat je code op zo'n tussenliggend niveau terecht komt. Als beginnende programmeur maak je er best een gewoonte van duidelijk te kiezen voor public of private.

    hashtag
    Reden van private

    Waarom zou je bepaalde zaken private maken? Het antwoord is opnieuw encapsulatie.

    Neem als voorbeeld de kilometerstand. Het is wettelijk verboden een kilometerstand zomaar aan te passen. Hij mag alleen veranderen doordat er met een auto gereden wordt. Dan is het logisch dat dit ook alleen maar op deze manier kan voor onze auto-objecten. We kunnen dit realiseren door kilometers van public naar private te wijzigen. Dan kunnen we de kilometerstand nog wijzigen, maar enkel door de (publieke) methode Rijden te gebruiken. Als we hem dan nog willen kunnen bekijken (maar niet rechtstreeks wijzigen), kunnen we een extra (publieke!) methode ToonKilometerstand voorzien.

    circle-info

    Volgens de conventie maken we dan van de grote "K" in kilometers een kleine "k", omdat publieke members met Pascal case genoteerd worden en private members met camel case.

    circle-info

    De richtlijnen rond naamgeving van Microsoft met betrekking tot attributen, methoden,... vind je terug.

    We kunnen ook methoden private maken. Dit gebeurt niet zo vaak als bij attributen, maar het kan wel. Dit doen we bijvoorbeeld als het gaat om een hulpmethode die binnen de klasse nuttig is, maar buiten de klasse fout gebruikt zou kunnen worden. Een typisch voorbeeld: een stukje code dat letterlijk herhaald wordt in twee of meer verschillende publieke methoden. In plaats van deze code te kopiëren, zonderen we ze af in een hulpmethode. Zo hoeven we eventuele bugfixes,... geen twee keer te doen.

    circle-info

    Een studente vroeg in een van de afgelopen jaren: "Kunnen we niet gewoon afspreken dat we van sommige zaken afblijven?" In principe wel. Python doet het min of meer zo. Langs de andere kant: als wij meedelen dat de examenvragen op een publieke website staan en dat je er niet naartoe mag surfen, zou niemand dat dan doen? Private velden aanpassen kan soms een goed idee lijken op korte termijn, maar een project saboteren op langere termijn.

    hashtag
    H10_3 SchoolAdmin Student Cursussen private

    We willen Coursesafschermen en vermijden dat er dubbele inschrijvingen zijn; namelijk dat een student meermaals voor dezelfde cursus wordt ingeschreven.

    hashtag
    Klassediagram

    Maak Coursesprivate.

    In C# schrijven we private members in camel case. Daarom wordt het attribuut hernoemd.

    Voeg een methode RegisterForCoursetoe. Enkel als de cursus waarvoor men wil registreren zich nog niet in coursesbevindt, wordt deze toegevoegd.

    Zorg ervoor dat in DemoStudentsde inschrijvingen voor een cursus gebeuren met behulp van de nieuwe methode.

    Properties

    Properties zijn een feature van C♯ om de leesbaarheid van code te verhogen. Ze zien er uit zoals attributen, maar werken zoals methoden.

    circle-info

    Properties behoren tot een algemenere categorie onderdelen van objecten genaamd members.

    circle-check

    . De camerabeelden zijn wat wazig, maar de schermopname is in orde.

    hashtag
    Properties

    In dit hoofdstuk bespreken we eerst waarom properties nuttig zijn. Vervolgens bespreken we de 2 soorten properties die er bestaan:

    1. Full properties

    2. Auto properties

    hashtag
    In een wereld zonder properties

    Stel dat we volgende klasse hebben:

    Stel nu dat we het benzinepeil van een auto als volgt proberen aanpassen:

    Misschien is de eerdere methode TankVol() te beperkt en willen we wel een willekeurige hoeveelheid benzine kunnen toevoegen of verwijderen, zo lang we niet minder dan 0l of meer dan 50l in de tank doen.

    Een eerste mogelijkheid is om hier methodes voor te schrijven:

    Dit gaat. De methodes zijn public, dus we kunnen ze overal oproepen. Bovendien verhindert de SetBenzine-methode ongeldige waarden. Het nadeel is dat de syntax om deze methodes te gebruiken zwaar is. Met publieke attributen konden we dit doen:

    Met de zogenaamde getter en setter moeten we dit doen:

    Het lijkt niet zo veel, maar code stapelt zich op doorheen de tijd. Properties lossen dit probleem op. Ze zorgen ervoor dat we kunnen "doen alsof" we publieke velden hebben, maar dezelfde hoeveelheid controle kunnen uitoefenen als met getters en setters.

    hashtag
    Full properties

    Een full property ziet er als volgt uit:

    Dankzij deze code kunnen we nu elders dit doen:

    Vergelijk dit met de vorige alinea waar we dit met Get en Set methoden moesten doen. Deze property syntax is veel eenvoudiger in het gebruik.

    We zullen de property nu stuk per stuk analyseren:

    • public double Benzine: Merk op dat we Benzine met een hoofdletter schrijven. Vaak wordt gekozen voor dezelfde naam als de variabele die we afschermen (in dit geval benzine met een kleine "b"), maar dan in Pascal case. Dat is niet strikt noodzakelijk. Je zou ook Naft kunnen schrijven. public en double staan er om dezelfde reden als in de oplossing met methodes GetBenzine() en SetBenzine(): we willen deze property buiten de klasse kunnen gebruiken en als we hem opvragen, krijgen we een double

    triangle-exclamation

    Let goed op dat je in je setter schrijft benzine = value en niet Benzine = value. Dat eerste past de verborgen instantievariabele aan. Dat tweede roept de setter opnieuw op. En opnieuw. En opnieuw. Probeer gerust eens een breakpoint te plaatsen voor de toekenning en dan de debugger te starten als je niet ziet waarom dit een probleem is.

    circle-info

    Visual Studio heeft een ingebouwde shortcut om snel een full property, inclusief een bijhorende private dataveld, te schrijven. Typ propfull gevolgd door twee tabs!

    hashtag
    Full property met toegangscontrole

    De full property Benzine heeft nog steeds het probleem dat we negatieve waarden kunnen toewijzen (via de set) die dan vervolgens zal toegewezen worden aan benzine.

    We kunnen in de set code extra controles inbouwen. Als volgt:

    Deze code zal het benzinepeil enkel aanpassen als het geldig is en anders stilletjes niets doen. Wat je vaak tegenkomt is throw new ArgumentException($"{value} is geen geldig benzinepeil"). Dit doet je programma crashen, maar legt ook uit waarom. We kunnen de code binnen set (en get) zo complex maken als we zelf willen.

    circle-exclamation

    Je kan dus extra controles toevoegen, maar deze hebben alleen zin als je de variabele via de property aanpast. Als je in een methode van de klasse auto benzine met kleine "b" aanpast en niet voorzichtig bent, kan je nog steeds een negatief peil instellen. Daarom wordt aangeraden ook binnen de klasse gebruik te maken van de property, dus zo veel mogelijk Benzine in plaats van benzine te gebruiken.

    hashtag
    Property variaties

    We zijn niet verplicht om zowel de get en de set code van een property te schrijven.

    Write-only property

    We kunnen dus enkel benzine een waarde geven, maar niet van buitenuit uitlezen.

    hashtag
    Read-only property

    Read-only property met private set

    Soms gebeurt het dat we van buitenuit enkel de gebruiker de property read-only willen maken. We willen echter intern (in de klasse zelf) nog steeds controleren dat er geen illegale waarden aan private datafields worden gegeven. Op dat moment definieren we een read-only property met een private setter:

    Van buitenuit zal enkel code werken die deget-van deze property aanroept: Console.WriteLine(auto.Benzine);. Code die de set van buitenuit nodig heeft zal een fout geven zoals: auto.Benzine=40.

    Read-only Get-omvormers

    Veel properties bieden toegang tot een achterliggende variabele van hetzelfde type, maar dat is niet verplicht. Je kan ook iets berekenen en dat teruggeven via een getter.

    Als we verder gaan met de klasse Auto:

    Je hebt iets gelijkaardigs gezien bij DateTime. Daar kan je allerlei stukjes informatie uit opvragen, maar eigenlijk wordt er maar één ding bijgehouden: het aantal "ticks". Dat zijn fracties van seconden sinds een bepaalde startdatum. Als je het uur of het aantal minuten of de maand of de weekdag of nog iets anders opvraagt via een DateTime, wordt deze ter plekke berekend uit het aantal ticks. Zo moet maar één stukje informatie worden bijgehouden, wat leidt tot minder fouten.

    hashtag
    Auto properties

    Automatische eigenschappen (autoproperties) in C# staan toe om eigenschappen (properties) die enkel een waarde uit een private variabele lezen en schrijven verkort voor te stellen. Ze zorgen dat je al snel properties hebt, maar staan je niet toe complexe berekeningen te doen of waarden te controleren zoals full properties dat wel doen.

    Voor Auto kan je bijvoorbeeld schrijven:

    Dit maakt achter de schermen een privé-attribuut aan zoals benzine met kleine "b", maar met een een verborgen naam. We kunnen dus niet rechtstreeks aan dat attribuut.

    triangle-exclamation

    Wij zullen geen gebruik maken van autoproperties, omdat het verschil met publieke attributen pas in meer geavanceerde scenario's zichtbaar wordt. We geven ze hier mee zodat je ze kan herkennen als je ze tegenkomt.

    hierarrow-up-right
    .
  • { }: Vervolgens volgen 2 accolades waarbinnen we de werking van de property beschrijven.

  • get {}: indien je wenst dat de property data naar buiten moet sturen, dan schrijven we de get-code. Binnen de accolades van de get schrijven we wat er naar buiten moet gestuurd worden. In dit geval return benzine maar dit mag even goed bijvoorbeeld return 4 of een hele reeks berekeningen zijn. Eigenlijk werkt dit net als de body van een methode en kan je hierin doen wat je in een methode kan doen.

    • We kunnen nu van buitenaf toch de waarde van benzine onrechtstreeks uitlezen via de property en het get-gedeelte: Console.WriteLine(auto.Benzine);

  • set {}: in het set-gedeelte schrijven we de code die we moeten hanteren indien men van buitenuit een waarde aan de property wenst te geven om zo een instantievariabele aan te passen. De waarde die we van buitenuit krijgen (eigenlijk is dit een parameter van een methode) zal altijd in een lokale variabele value worden bewaard. Deze zal van het type van de property zijn. In dit geval dus double, want het type bij Benzine is double. Vervolgens kunnen we value toewijzen aan de interne variabele indien gewenst: benzine=value .

  • Kennisclip voor deze inhoudarrow-up-right
    public class Auto
    {
        private int kilometers;
        private double benzine;
    }
    Auto auto = new Auto();
    auto.benzine += 10; //DIT ZAL DUS NIET WERKEN, daar benzine private is.
    public class Auto
    {
        private int kilometers;
        private double benzine;
        public double GetBenzine() {
            return this.benzine;
        }
        public void SetBenzine(double waarde) {
            if(waarde >= 0 && waarde <= 50) {
                this.benzine = waarde;
            }
        }
    }
    Auto auto = new Auto();
    auto.Benzine += 10;
    Auto auto = new Auto();
    auto.SetBenzine(auto.GetBenzine() + 10);
    class Auto
    {
        private int kilometers;
        private double benzine;
    
        public double Benzine
        {
            get
            {
                return benzine;
            }
            set
            {
                benzine = value;
            }
        }
    }
    Auto auto = new Auto();
    auto.Benzine = 20; //set
    Console.WriteLine($"Het benzinepeil is {auto.Benzine}"); //get
       public double Benzine
        {
            get
            {
                return benzine;
            }
            set
            {
                if(value >= 0 and value <= 50) {
                    benzine = value;
                }
            }
        }
       public double Benzine
        {
            set
            {
                if(value >= 0) {
                    benzine = value;
                }
            }
        }
       public double Benzine
        {
            get
            {
                return benzine;
            }
        }
       public double Benzine
        {
            get
            {
                return benzine;
            }
            private set
            {
                if(value >= 0) {
                    benzine = value;
                }
            }
        }
    public class Auto
    {
        private int kilometers;
        private double benzine;
        // stelt het aantal blokjes benzine voor op je display
        // bij 50l heb je 5 blokjes
        // bij tussen 40 en 50l heb je 4 blokjes
        // ...
        // bij minder dan 10l heb je 0 blokjes
        public int Blokjes {
            get {
                return Math.Floor(this.benzine / 10);
            }
        }
    }
    public class Auto
    {
        public double Benzine
        { get; set; }
    }

    DateTime: leren werken met objecten

    circle-check

    Kennisclip. Klein verschil met de pagina tot de twee oranje checkboxes, dus lees die goed.arrow-up-right

    hashtag
    DateTime

    Het .NET gegevenstype DateTime is de ideale manier om te leren werken met objecten. Het is een nuttig en toegankelijk gegevenstype. Je kan er je iets bij voorstellen, maar het is ook een beetje abstract.

    circle-exclamation

    Waarom spreken we hier over "gegevenstype" en niet over "klasse"? Omdat klassen in .NET reference types zijn. DateTime is echter een value type, dus technisch gezien is het een "struct" en geen "klasse". Wij zullen zelf geen structs schrijven, maar het verschil met klassen is uiterst klein in C#. Instanties van zowel klassen als structs zijn objecten.

    circle-exclamation

    Zegt het verschil tussen value types en reference types je niets meer? Kijk dan terug naar .

    hashtag
    DateTime objecten aanmaken

    Er zijn 2 manieren om DateTime objecten aan te maken:

    1. Door aan de klasse de huidige datum en tijd te vragen via DateTime.Now

    2. Door manueel de datum en tijd in te stellen via de constructor

    hashtag
    DateTime.Now

    Volgend voorbeeld toont hoe we een object kunnen maken dat de huidige datum tijd van het systeem bevat. Vervolgens printen we dit op het scherm:

    DateTime op het begin van regel 1 is het type van de variabele. Dit type drukt uit met wat voor data we te maken hebben. Dit is net dezelfde redenering als bij het gebruik van string in string mijnTekst = "hello world";

    hashtag
    Met constructor

    Via de constructor kunnen we beginwaarden meegeven bij het maken van een nieuw object. Er zijn 11 manieren waarop dit kan zoals je .

    Enkele voorbeelden:

    circle-info

    Je hebt eerder al met constructoren gewerkt: herinner je new Random(). Hiermee maakte je eigenlijk een object aan dat willekeurige getallen kon genereren.

    hashtag
    DateTime methoden

    Ieder DateTime object dat je aanmaakt heeft een hoop nuttige methoden.

    hashtag
    Add Methods

    Deze methoden kan je gebruiken om een bepaalde aantal dagen, uren, minuten en zo voort aan je huidige object toe te voegen. Al deze methoden geven steeds een nieuw DateTime object terug dat je moet bewaren wil je er iets mee doen:

    • AddDays

    • AddHours

    • AddMilliseconds

    Een voorbeeld:

    (voorgaande kan ook in 1 lijn: DateTime nextWeek= DateTime.Now.AddDays(7))

    Uiteraard mag je ook een bestaand object overschrijven met het resultaat van deze methoden:

    hashtag
    DateTime properties

    Properties zijn een zeer uniek aspect van C#. Ze geven toegang tot de data van een object, maar op een gecontroleerde manier. We zullen deze nog tot in den treure leren maken.

    Enkele nuttige properties van DateTime zijn:

    • Date

    • Day

    • DayOfWeek

    hashtag
    Properties gebruiken

    Alle properties van DateTime zijn read-only. Je kan een bestaande datum dus niet aanpassen (maar je kan wel een nieuwe datum baseren op een bestaande datum).

    Een voorbeeld:

    Uiteraard mag je ook deze properties gebruiken om direct naar het scherm te schrijven:

    hashtag
    Datum en tijd formatteren

    Je hebt een invloed op hoe DateTime objecten naar string worden opgezet. Omzetten naar een string is functionaliteit van het object. Je doet dit dus via een objectmethode. Deze gebruik je zoals de statische methoden van eerder, maar je roept ze op voor een specifiek object.

    Je kan bepalen hoe de omzetting naar string moet gebeuren door extra formatter syntax mee te geven. Dit zie je in volgende voorbeeld:

    circle-info

    hashtag
    Custom format

    Wil je nog meer controle over de output dan kan je ook zelf je formaat specifieren.

    hashtag
    Localized time

    De manier waarop DateTime objecten worden getoond (via ToString) is afhankelijk van de landinstellingen van je systeem. Soms wil je dit echter op een andere manier tonen. Je doet dit door mee te geven volgens welke culture de tijd en datum getoond moet worden.

    Dit vereist dat je eerst een CultureInfo object aanmaakt en dat je dan meegeeft:

    circle-info

    Culture names

    Een lijst van alle cultures in .NET kan je .

    Opgelet, enkel indien een specifieke culture op je computer staat geïnstalleerd zal je deze kunnen gebruiken.

    CultureInfo is een klasse waar je je misschien al iets minder bij kan voorstellen dan DateTime. Een CultureInfo stelt een aantal afspraken voor een bepaalde cultuur voor. De data is de combinatie van taal en regio. De functionaliteit bestaat er dan in om zaken zoals de munt, het gebruikte formaat voor datums,... in die taal en die regio te produceren. Ook hier geldt dus het black box principe: je kan code schrijven die bijvoorbeeld het juiste datumformaat voor Rusland gebruikt wanneer je zelf niet weet wat dat formaat is.

    hashtag
    Static method

    Sommige methoden zijn static dat wil zeggen dat je ze enkel rechtstreeks op de klasse kunt aanroepen. Vaak zijn deze methoden hulpmethoden waar de individuele objecten niets aan hebben.

    hashtag
    Parsing time

    Parsen laat toe dat je strings omzet naar DateTime. Dit is handig als je bijvoorbeeld de gebruiker via ReadLine tijd en datum wilt laten invoeren:

    Zoals je ziet roepen we Parse aan op DateTime en dus niet op een specifiek object. Dit is logisch omdat je geen bestaande datum nodig hebt om een nieuwe datum te parsen.

    hashtag
    IsLeapYear

    Deze nuttige methode geeft een bool terug om aan te geven het meegegeven object eens schrikkeljaar is of niet:

    Dit is logisch omdat je geen volledige bestaande datum nodig hebt. Je wil gewoon iets zeggen over een jaartal, terwijl een DateTime ook een dag, maand, uur,... heeft. Dus dit heeft niet veel te maken met een specifieke DateTime, maar heeft duidelijk wel met de klasse te maken.

    circle-info

    Af en toe is het een kwestie van smaak van de auteurs of een methode statisch is of niet. Maar meestal is er een duidelijke "beste" keuze.

    hashtag
    TimeSpan

    Je kan DateTime objecten ook bij mekaar optellen en aftrekken. Deze bewerking geeft echter geen DateTime object terug, maar een TimeSpan object. Dit is een object dat dus aangeeft hoe groot het verschil is tussen de 2 DateTime objecten:

    AddMinutes

  • AddMonths

  • AddSeconds

  • AddTicks

  • AddYears

  • DayOfYear

  • Hour

  • Millisecond

  • Minute

  • Month

  • Second

  • Ticks

  • TimeOfDay

  • Today

  • UtcNow

  • Year

  • deze paginaarrow-up-right
    hier kan zienarrow-up-right
    Dit wordt hier volledig uit de doeken gedaan.arrow-up-right
    hier terugvindenarrow-up-right
    DateTime currentTime = DateTime.Now;
    Console.WriteLine(currentTime);
    DateTime birthday = new DateTime(1982, 3, 18); //year, month, day
    DateTime someMomentInTime = new DateTime(2017, 1, 18, 10, 16,34 ); //year, month, day, hour, min, sec
    DateTime timeNow= DateTime.Now;
    DateTime nextWeek= timeNow.AddDays(7);
    DateTime someTime= new DateTime(2019, 4, 1);
    // much later...
    someTime = someTime.AddYears(10);
    Console.WriteLine(someTime);
    DateTime moment = new DateTime(1999, 1, 13, 3, 57, 32, 11);
    // Year gets 1999.
    int year = moment.Year;
    // Month gets 1 (January).
    int month = moment.Month;
    // Day gets 13.
    int day = moment.Day;
    // Hour gets 3.
    int hour = moment.Hour;
    // Minute gets 57.
    int minute = moment.Minute;
    // Second gets 32.
    int second = moment.Second;
    // Millisecond gets 11.
    int millisecond = moment.Millisecond;
    DateTime now = DateTime.Now;
    Console.WriteLine($"The current day is {now.DayOfWeek}");
    DateTime now = DateTime.Now;
    WriteLine(now.ToString("d")); // short date 
    WriteLine(now.ToString("D")); // long date
    WriteLine(now.ToString("F")); // full date and time
    WriteLine(now.ToString("M")); // month and day
    WriteLine(now.ToString("o")); // date en time separated by T and time zone at the end
    WriteLine(now.ToString("R")); // RFC1123 date and time
    WriteLine(now.ToString("t")); // short time
    WriteLine(now.ToString("T")); // long time
    WriteLine(now.ToString("Y")); // year and month
    DateTime now = DateTime.Now;
    CultureInfo russianCI = new CultureInfo("ru-RU");
    Console.WriteLine($"Current time in Russian style is: {now.ToString("F", russianCI)}");
    string date_string = "8/11/2016"; //dit zou dus ook door gebruiker kunnen ingetypt zijn
    DateTime dt = DateTime.Parse(date_string);
    Console.WriteLine(dt);
    DateTime today = DateTime.Now;
    bool isLeap = DateTime.IsLeapYear(today.Year);
    if(isLeap == true) {
        Console.WriteLine("This year is a leap year");
    }
    DateTime today = DateTime.Today;
    DateTime borodino_battle = new DateTime(1812, 9, 7);
    TimeSpan diff = today - borodino_battle;
    WriteLine("{0} days have passed since the Battle of Borodino.", diff.TotalDays);