Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 143 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

experimentalmerge5

Loading...

Inleiding

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Semester 1: Programming Principles

H0: Werken met Visual Studio

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

H1: Variabelen en datatypes

Loading...

Loading...

Loading...

Loading...

Loading...

H2: Tekst in code

Loading...

Loading...

Loading...

Loading...

Loading...

H3: Werken met data

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

H4: Beslissingen

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

H5: Loops

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

H6: Methoden

Loading...

Loading...

Loading...

Loading...

Loading...

H7: Arrays

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Semester 2 : OOP

H8: Klassen en objecten

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

H9: Geheugenmanagement bij klassen

Loading...

Loading...

Loading...

Loading...

H10: Geavanceerde klassen en objecten

Loading...

Loading...

Loading...

Loading...

H11: Arrays en klassen

Loading...

Loading...

Loading...

Loading...

Loading...

H12: Overerving

Loading...

Loading...

Loading...

Loading...

Loading...

H13: Geavanceerde overerving

Loading...

Loading...

Afspraken oefeningen

Voor de graduaatsopleiding hanteren we een vaste structuur voor de oefeningen.

vorm van de oefeningen

unieke titel

Elke oefening begint met een unieke titel. Deze gebruik je voor de organisatie van je bestanden.

korte toelichting

Eerst krijg je een omschrijving in tekst van wat de algemene bedoeling is van je programma. Deze bevat niet noodzakelijk elk detail, maar geeft je een big picture. Bijvoorbeeld:

In deze oefening maak je een echo. Dit is een programma dat tekst inleest vanaf het toetsenbord en deze tekst dan op het scherm toont.

vertrekpunt

Je hoeft niet altijd alle code zelf te schrijven. Code die je nog niet verondersteld wordt zelf te kunnen schrijven, of code die gewoon veel tijd in beslag neemt, krijg je. Bij de stukken code staat telkens de naam van het bestand waarin ze thuishoort. Commentaar bij de te wijzigen stukken code geeft aan welke aanpassingen je moet doen. Bijvoorbeeld:

Program.cs

using System;

namespace Programmeren {
    class Program {
        public static void Main(string[] args) {
            // zorg er zelf voor dat een regel tekst die gebruiker intypt weer verschijnt
            string message;
            Console.WriteLine(message);
        }
    }
}

voorbeeldinteractie

Ten slotte worden één of meerdere voorbeelden getoond van hoe je programma zich gedraagt. In deze voorbeelden stellen regels die niet beginnen met > iets voor dat op het scherm verschijnt. Regels die wel beginnen met > stellen iets voor dat door de gebruiker zelf wordt ingetypt. Bijvoorbeeld, voor het programma dat we hier als leidraad gebruiken:

> Hallo, wereld!
Hallo, wereld!

We gebruiken de speciale syntax <EOF> om aan te geven dat er een end-of-file signaal wordt gestuurd.

organisatie van je bestanden

Voor de eenvoud zorgen we dat elke oefening op zichzelf staat. Dit betekent dat sommige code herhaald wordt in je bestanden, maar het vermijdt dat iets dat je in week 1 geschreven hebt in week 5 plots niet meer werkt. Concreet doen we dat zo:

  • Je maakt ergens op je machine één map voor alle oefeningen van het vak Programmeren (in semester 1) of Objectgericht programmeren (in semester 2). Het maakt niet uit waar, maar je kiest één map en je blijft deze heel het semester gebruiken.

  • Voor elke oefening in het eerste semester maak je in die map een submap. De naam van deze submap is letterlijk de unieke titel van de oefening, inclusief gebruik van hoofdletters, leestekens,... Je voegt geen andere structuur toe, dus geen mapjes per labosessie,...

  • Wanneer een oefening verderbouwt op een eerdere oefening, kopieer en hernoem je de map van die eerdere oefening. Daarna werk je dan voort in die nieuwe map.

Nuttige extras

Nuttige extra's

Boeken

Er zijn quasi oneindig veel boeken over C# geschreven, althans zo lijkt het. Hier een selectie van boeken met een korte bespreking waarom ik denk dat ze voor jou een meerwaarde kunnen zijn bij het leren programmeren in C#:

Beginner boeken

Geavanceerd

Online

Leren programmeren door enkele de opdrachten in deze cursus te maken zal je niet ver (genoeg) brengen. Onze dikke vriend het Internet heeft echter tal van schitterende bronnen. Hier een overzicht.

Cheat sheet

Game-based programmeren

Ideale manier om programmeren meer in de vingers te krijgen op een speelse manier:

Apps

Websites

Tutorials

Oefenvragen

  • Pittige vragen van de jaarlijkse Vlaamse Programmeerwedstrijd:

Streaming programmeurs

Ja hoor, ze bestaan. Meer en meer professionele én beginnende programmeurs streamen terwijl te programmeren. Dit is een ideale manier om te zien hoe andere mensen problemen aanpakken. De meeste programming streamers kan je terugvinden op youtube, maar ook op Twitch zijn er steeds meer. Enkele aanraders (bekijk zeker de filmpjes uit de archieven eens):

van Mike McGrath: een uiterst compact, maar zeer helder en kleurrijk boekje dat ik ten stelligste aanbeveel als je wat last hebt met de materie van de eerste weken.

van Joyce Farrell: Niet het meest sexy boek, maar wel het meest volledige qua overlap met de leerstof van deze cursus. Aanrader voor zij die wat meer in detail willen gaan en op zoek zijn naar oneindig veel potentiele examenvragen ;)

van Andrew Stellman & Jennifer Greene: laat de ietwat bizarre, bijna kleuterachtige look and feel van de head first boeken je niet afschrikken. Ieder boek in deze serie is goud waar. De head first boeken zijn d� ideale manier als je zoekt naar een alternatieve manier om complexe materie te begrijpen. Bekijk zeker ook de Head First Design Patterns en Head First Sql boeken in de reeks!

van Bart De Smet: in mijn opinie dé referentie om C# tot op het bot te begrijpen. Geschreven door een Belg die bij Microsoft in Redmond aan C# werkt.

van Steve McConnell: een referentiewerk over 'programmeren in het algemeen'. Het boek is al jaar en dag het te lezen boek als je je als programmeur wilt verdiepen in wat nu 'correct programmeren' behelst. Als je op je CV kunt zetten dat je dit boek door en door kent dan zal elk IT-bedrijf je stante pede aannemen ;)

Volgende document bevat een overzicht van de basis C# syntax zaken van het eerste en (deel van het) tweede semester:

: Verplichte app! Simple as that!

Net zoals SoloLearn maar dan anders.

(aanrader!) Origineel vooral bedoeld om spreektalen te leren, maar bevat ook tal van andere zaken. Hoofdzakelijk nuttig om nieuwe aspecten te 'drillen'. Enkel dus nuttig indien je de basismaterie eerst hebt verwerkt. Bekijk zeker ook de tal van andere cursussen die er staan. Let er op dat je bij de filter Engels instelt, er zijn nog niet veel (goede) Nederlandstalige C# cursussen naar mijn weten. Opgelet: je kan je enkel via de browser inschrijven op niet-spreektaal-cursussen. De app toont enkel spreektaalcursussen.

Speels en vrij beperkt in gratis versie, maar ideale aanvulling op SoloLearn.

Een steam spel om te leren programmeren. Weliswaar Javascript (nuttig voor Web Programming) maar het concept is te cool om niet hier te vermelden en zoals je zal ontdekken: leren programmeren kan je in eender welke taal, en het zal ook je andere programmeer-ervaring verbeteren. Give it a go!

zeer vet

(specifiek voor C#!)

(behandelt leerstof van volledig eerste jaar en meer)

Pittige programmeeroefeningen die jaarlijks in december verschijnen.

Handig vorm gegeven gratis ebooks met tal van onderwerpen waaronder ook C# en het .NET Framework.

: De uitgebreidere, Engelstalige variant van deze cursus zeg maar.

: Aanrader.

: Zeer aan te raden indien je een bepaald concept uit de les niet begrijpt.

: Nederlandstalige cursus met veel toffe oefeningen waarvan je sommige zelfs in deze cursus zal terugvinden.

: Microsoft heeft een virtual academy cursus "C# fundamentals" uitgebracht. Ik kan deze cursus zeer erg aanbevelen.

: Zeer vermakelijk, vlot geschreven C# boek(je)

: "This site tries to gather open-source remakes of great old games in one place." Je vindt er ook tal van C# projecten terug zoals .Klik bovenaan op "languages" en filter maar eens op C#.

: nog steeds relevant.

: deze programmeur heeft een volledige RPG gemaakt en het hele proces gestreamd.

C# Programming
Microsoft Visual C# 2015: An introduction to OOP
Head First C#
C# Unleashed
Code Complete
download hier
SoloLearn
Enki
Memrise
Mimo
Screeps
Exercism
Coding game
Code Combat
Pex For Fun
Code Academy
RPG Game in C#
Advent of code
Free Programming Book
Tutorials teacher
Dotnet beginning
C# Getting started interactive quickstart tutorials
Online video c# cursus
C-sharp.be
Microsoft Virtual Academy
Rob Miles's The C# Programming Yellow book
Open Source Game Clones
GTA 2
Een lijst met oude oefenvragen uit 2010
Veel kleine, fijne oefeningen
Een dagelijkse programmeeruitdaging op reddit
2013
2014
2015
Handmade Hero
CSharpFrits
DevChatter
Visual Studio Twitch
NoopKat
The Coding train

Introductie tot C#

Introductie tot programmeren met C-Sharp

Het algoritme

De essentie van een computerprogramma is het zogenaamde algoritme (het "recept" zeg maar). Dit is de reeks instructies die het programma moet uitvoeren telkens het wordt opgestart. Het algoritme van een programma moet je zelf verzinnen. De volgorde waarin de instructies worden uitgevoerd zijn uiteraard zeer belangrijk. Dit is exact hetzelfde als in het echte leven: een algoritme om je fiets op te pompen kan zijn:

1. Haal dop van het ventiel
2. Plaats pomp op ventiel
3. Begin te pompen

Eender welke andere volgorde van bovenstaande algoritme zal vreemde (en soms fatale) fouten geven.

C-Sharp

Om een algoritme te schrijven dat onze computer begrijpt dienen we een programmeertaal te gebruiken. Net zoals er ontelbare spreektalen in de wereld zijn, zijn er ook vele programmeertalen. C# (spreek uit 'siesjarp') is er een van de vele. In tegenstelling tot onze spreektalen moet een computertaal 'exact' zijn en moet het op ondubbelzinnige manier door de computer verstaan worden. C# is een taal die deel uitmaakt van de .NET (spreek uit 'dotnet') omgeving die meer dan 15 jaar geleden door Microsoft werd ontwikkeld (juli 2000).

Microsoft .NET

Bij de geboorte van .NET in 2000 zat dus ook de taal C#. .NET is een zogenaamd framework. Dit framework bestaat uit een grote groep van bibliotheken (class libraries) en een virtual execution system genaamd de Common Language Runtime (CLR). De CLR zal ervoor zorgen dat C#, of andere .NET talen (F#, VB.NET, etc.), kunnen samenwerken met de vele bibliotheken.

Om een uitvoerbaar bestand te maken (executable, vandaar de extensie .exe bij uitvoerbare programma's in windows) zal de broncode die je hebt geschreven in C# worden omgezet naar Intermediate Language (IL) code. Op zich is deze IL code nog niet uitvoerbaar, maar dat is niet ons probleem. Wanneer een gebruiker een in IL geschreven bestand wil uitvoeren dan zal, achter de schermen, de CLR deze code ogenblikkelijk naar machine code omzetten (Just -In-Time of JIT compilatie) en uitvoeren. De gebruiker zal dus nooit dit proces merken (tenzij er geen .NET framework werd geïnstalleerd op het systeem).

Merk op dat we veel details van de compiler achterwege laten hier. De compiler is een uitermate complex element, maar in deze fase van je (prille) programmeursleven hoeven we enkel de kern van de compiler te begrijpen: het omzetten van C# code naar een uitvoerbaar bestand geschreven in IL code.

Deze cursus heeft als doel om je de programmeertaal C# aan te leren. Terwijl we dit doen zullen we ook geregeld andere .NET framework gerelateerde zaken aanraken.

Werken met Visual Studio

Kennismaken met C# en Visual Studio

Visual Studio (VS) is een pakket dat een groot deel tools samenvoegt (debugger, code editor, compiler, etc) zodat je niet tientallen paketten moet gebruiken om software te schrijven.

Visual Studio is een zogenaamde IDE("Integrated Development Environment") en is op maat gemaakt om C#.NET applicaties te ontwikkelen. Je bent echter verre van verplicht om enkel C# applicaties in VS te ontwikkelen, je kan gerust VB.NET, TypeScript, Python en andere talen gebruiken.

De compiler en Visual Studio

Jouw taak als programmeur in deze cursus is algoritmes in C# taal uitschrijven. We zouden dit in een eenvoudige tekstverwerker kunnen doen, maar dan maken we het onszelf lastig. Net zoals je tekst in notepad kunt schrijven, is het handiger dit in bijvoorbeeld Word te doen: je krijgt een spellingchecker en allerlei handige extra's. Ook voor het schrijven van computer code is het handiger om een zogenaamde IDE te gebruiken, een omgeving die ons zal helpen foutloze C# code te schrijven.

Het hart van Visual Studio bestaat uit de compiler die we hiervoor besproken. De compiler zal je C# code omzetten naar de IL-code zodat jij (of anderen) je applicatie op een computer (of ander apparaat) kunnen gebruiken. Zolang de C# niet exact voldoet aan de C# syntax (zie verder) zal de compiler het vertikken een uitvoerbaar bestand voor je te genereren.

Visual Studio Installeren

Het is belangrijk bij de installatie dat je minimaal

  • Bij individual components de "Class Designer" aanduiden

  • Uiteraard ben je vrij om meerdere zaken te installeren.

Visual studio opstarten

Na het opstarten van VS krijg je het startvenster te zien van waaruit je verschillende dingen kan doen.

Een nieuw project aanmaken

We zullen nu een nieuw project aanmaken, kies hiervoor "Create a new project".

Het "New Project" venster dat nu verschijnt geeft je hopelijk al een glimp van de veelzijdigheid van VS. In het rechterdeel zie je bijvoorbeeld alle Project Types staan. M.a.w. dit zijn alle soorten programma’s die je kan maken in VS. Naargelang de geïnstalleerde opties en bibliotheken zal deze lijst groter of kleiner zijn.

Dit semester kiezen we steeds als Project Type Console App (.NET Core). Kies dit type en klik 'Next'.

Op het volgende scherm kan je een naam geven voor je project alsook de locatie op de harde schijf waar het project dient opgeslagen te worden. Onthoudt waar je je project aanmaakt zodat je dit later terugvindt.

De solution name blijf je af (deze moet momenteel dezelfde naam zijn als je project)

Geef je projectnamen ogenblikkelijk duidelijke namen zodat je niet opgezadeld geraakt met projecten zoals Project201.

Geef je project de naam "MyFirstProject" en kies een goede locatie (ik raad je aan dit steeds in Dropbox of Onedrive te doen)

Klik nu op create.

VS heeft nu reeds een aantal bestanden aangemaakt die je nodig hebt om een ‘Console Applicatie’ te maken. Een console applicatie is een programma dat alle uitvoer naar een zogenaamde ‘console’ stuurt, een shell. M.a.w., je kan enkel tekst (Unicode) als uitvoer genereren en dus geen multimedia elementen zoals afbeeldingen, geluid, etc.

IDE Layout

Wanneer je VS opstart zal je mogelijk overweldigd worden door de hoeveelheid menu's, knopjes, schermen, etc. Dit is normaal voor een IDE: deze wil zoveel mogelijk mogelijkheden aanbieden aan de gebruiker. Vergelijk dit met Word: afhankelijk van wat je gaat doen gebruikt iedere gebruiker andere zaken van Word. De makers van Word kunnen dus niet bepaalde zaken weglaten, ze moeten net zoveel mogelijk aanbieden.

Laat je niet afschrikken door VS. Het is een imponerend programma, maar je zal er sneller dan je verwacht, je weg in terugvinden!

We zullen nu eerst eens bekijken wat we allemaal zien in VS na het aanmaken van een nieuw programma.

  • Je kan meerdere bestanden tegelijkertijd openen in VS. Ieder bestand zal z'n eigen tab krijgen. De actieve tab is het bestand wiens inhoud je in het hoofdgedeelte eronder te zien krijgt. Merk op dat enkel open bestanden een tab krijgen.

  • De "solution explorer" toont alle bestanden en elementen die tot het huidige project behoren. Als we dus later nieuwe bestanden toevoegen dan kan je die hier zien (en openen).

  • Het properties venster (eigenschappen) is een belangrijk venster. Hier komen alle eigenschappen van het huidige geselecteerde element. Selecteer bijvoorbeeld maar eens Program.cs in de solution explorer en merk op dat er allerlei eigenschappen getoond worden. Onderaan het Properties venster wordt steeds meer informatie getoond over de huidig geselecteerde eigenschap.

Layout kapot/kwijt/vreemd

De layout van VS kan je volledig naar je hand zetten. Je kan ieder (deel-)venster en tab verzetten, verankeren en zelfs verplaatsen naar een ander bureaublad. Experimenteer hier gerust mee en besef dat je steeds alles kan herstellen. Het gebeurt namelijk al eens dat je layout een beetje om zeep is:

  • Om eenvoudig een venster terug te krijgen, bijvoorbeeld het properties window of de solution explorer: klik bovenaan in de menubalk op "View" en kies dan het gewenste venster (soms staat dit in een submenu).

  • Je kan ook altijd je layout in z'n geheel resetten: ga naar "Window" en kies "Reset window layout".

Je programma starten

De code die VS voor je heeft gemaakt is reeds een werkend, maar weinig nuttig, programma. Je kan de code compileren en uitvoeren door op de groene driehoek bovenaan te klikken:

Als alles goed gaat krijg je nu "Hello world" te zien en wat extra informatie omtrent het programma dat net werd uitgevoerd:

Veel doet je programma nog niet natuurlijk, dus sluit dit venster maar terug af door een willekeurige toets in te drukken.

Is dit alles?

Liever VS Enterprise?

Kennisclip

Deze cursus werd geschreven in . Helaas ondersteunt deze geen # in titels. Daardoor heet dit hoofdstuk "C-Sharp" en niet "C#". Niets aan te doen.

De geschiedenis en de hele .NET-wereld vertellen zou een cursus op zich betekenen en gaan we hier niet doen. Het is nuttig om weten dat er een gigantische bron aan informatie over .NET en C# online te vinden is, beginnende met .

In deze cursus zullen we steeds werken met Visual Studio. Niet met Visual Studio Code. Visual Studio code is een zogenaamde lightweight versie van VS die echter zeker ook z'n voordelen heeft (gratis, snel, compact, etc). Visual Studio vindt dankzij VS Code eindelijk ook z'n weg op andere platformen dan enkel die van Microsoft. Zoek je een lightweight versie dan moet je zeker eens eens proberen.

In deze cursus zullen de voorbeelden steeds met de Community editie van VS gemaakt zijn. Je kan deze als AP-student gratis downloaden en installeren via .

de .NET desktop development en .NET Core cross-platform development workload selecteert als te installeren tools.

Nee hoor. Visual Studio is lekker groot. Was bovenstaande uitleg toch niet zo verhelderend als ik hoopte? Bekijk dan volgende korte, maar zeer duidelijke uitleg over Visual Studio en de verschillende onderdelen (klik zeker op de chapters in het linkermenu om verder te lezen): .

Als AP-student heb je ook recht op een Visual Studio Enterprise licentie. Wil je deze gebruiken, download deze dan via en zorg ervoor dat je een key aanvraagt via de tab 'Software' op . Je kan deze sleutel vervolgens via Help->Register in VS invoeren.

Markdown
docs.microsoft.com
(Bron afbeelding)
Visual Studio Code
visualstudio.microsoft.com/vs
hier
https://visualstudio.microsoft.com/vs/
Azure for education
De VS omgeving
De folderstructuur van projecten

Benodigdheden

In alle lessen (hoorcollege en practica) hebben we 2 zaken nodig:

  1. Deze cursus

  2. Een laptop met daarop Visual Studio 2019 Community editie geïnstalleerd.

Dankwoord

Deze cursus is een fork van . We zijn Tim Dams en al zijn co-auteurs dank verschuldigd voor de stevige basis. Je mag veronderstellen dat alles Tims werk is, wij hebben vooral de vorm van de oefeningen aangepast voor een meer beroepsgerichte opleiding.

die van de richting toegepaste informatica

Fouten in je code

Indien je op de groene start knop duwt en volgende waarschuwing krijgt KLIK DAN NOOIT OP YES EN DUID NOOIT DE CHECKBOX AAN:

Lees de boodschap eens: wat denk je dat er gebeurt als je op 'yes' duwt? Inderdaad, VS zal de laatste goed gelukte code uitvoeren en dus niet de code die je nu hebt staan waarin nog fouten staan.

Fouten vinden

Zolang er dus fouten in je code staan moet je deze eerst oplossen voor je verder kan. Gelukkig helpt VS je daarmee op 2 manieren:

  • Fouten in code worden met een rode squigly onderlijnt.

  • Onderaan zie je in de statusbalk of je fouten hebt.

Fouten vinden

Uiteraard ga je vaak code hebben die meerdere schermen omvat. Je kan via de error-list snel naar al je fouten gaan. Open deze door op het error-icoontje onderaan te klikken:

In deze list kan je nu op iedere error klikken om ogenblikkelijk naar de correcte lijn te gaan.

Fouten oplossen

Wanneer je je cursor op een lijn met een fout zet dan zal je soms vooraan een geel error-lampje zien verschijnen:

Je kan hier op klikken en heel vaak krijg je dan ineens een mogelijke oplossing. Wees steeds kritisch hierover want VS is niet alomwetend en kan niet altijd raden wat je bedoelt. Neem dus het voorstel niet zomaar over zonder goed na te denken of het dat was wat je bedoelde.

Meest voorkomende fouten

De meest voorkomende fouten in deze eerste weken zullen zijn:

  • Puntkomma vergeten.

  • Schrijffouten in je code RaedLine i.p.v. ReadLine.

  • Geen rekening gehouden met hoofdletter gevoeligheid Readline i.p.v. ReadLine (zie volgende hoofdstuk).

  • Per ongeluk accolades verwijderd.

  • Code geschreven op plekken waar dat niet mag (je mag enkel binnen de accolades van Main schrijven).

Je code kan niet gecompileerd en uitgevoerd worden zolang er fouten in je code staan (dit bespraken we reeds in ).

een eerder hoofdstuk

Oefeningen

Oefening: H0-eerste-programma

Leerdoelen

  • een eigen programma kunnen uitvoeren

  • input en output via Console.ReadLine en Console.WriteLine

Functionele analyse

Binnen een zgn. dos-box wordt een titel weergegeven, nl. dit is mijn eerste c# programma.

Vervolgens wordt gevraagd je naam te noteren.

Wanneer je je naam hebt genoteerd en op enter hebt gedrukt, verschijnt de tekst “hallo [en je ingegeven naam]”.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Technische hulp

Programmaverloop

Wat het lezen en schrijven van tekst betreft moet gebruik gemaakt worden Console.WriteLine en Console.ReadLine.

Testscenario's

  • Probeer meer dan 200 tekens in te voeren

  • Probeer geen tekst in te voeren

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H0-eerste-programma-pro

Leerdoelen

  • een eigen programma kunnen uitvoeren

  • input en output via Console.ReadLine en Console.WriteLine

  • herhaling van de leerdoelen uit H0-eerste-programma

Functionele analyse

Binnen een zgn. dos-box wordt een titel weergegeven, nl. dit is mijn eerste c# programma.

Vervolgens wordt gevraagd je voornaam te noteren. Wanneer je je voornaam hebt genoteerd en op enter hebt gedrukt, wordt gevraagd je achternaam te noteren.

Wanneer je je achternaam hebt genoteerd en op enter hebt gedrukt, verschijnt de tekst “dus je naam is: [en je ingegeven achternaam en voornaam]”. Op de regel daaronder verschijnt dan de tekst “of: [en je ingegeven voornaam en achternaam]”.

Het is dus duidelijk dat we de naam zowel beginnend met de voor- als de achternaam kunnen tonen.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Technische hulp

Programmaverloop

Wat het lezen en schrijven van tekst betreft moet gebruik gemaakt worden Console.WriteLinea en Console.ReadLine.

Testscenario's

  • Probeer meer dan 200 tekens in te voeren

  • Probeer geen tekst in te voeren

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H0-rommelzin

Leerdoelen

  • een eigen programma kunnen uitvoeren

  • input en output via Console.ReadLine en Console.WriteLine

  • de computer leren zien als "domme verwerker"

Functionele analyse

Dit programma verwerkt tekst die door de gebruiker wordt ingetypt. Het print nieuwe berichten die deze tekst bevatten uit. Het print niet de berichten die je verwacht: het zal de antwoorden door elkaar halen en je favoriete kleur tonen wanneer het beweert je favoriete eten te tonen, enzovoort. De verbanden worden duidelijk uit de voorbeeldinteractie.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Wat is je favoriete kleur?
> blauw
Wat is je favoriete eten?
> spaghetti
Wat is je favoriete auto?
> Toyota Aygo
Wat is je favoriete film?
> Robocop 2
Wat is je favoriete boek?
> The Gone-Away World
Je favoriete kleur is spaghetti. Je eet graag Toyota Aygo. Je lievelingsfilm is The Gone-Away World en je favoriete boek is Robocop 2.

Technische hulp

Programmaverloop

Per regel die getoond wordt op het scherm, maak je gebruik van Console.WriteLine. Per regel die je zelf intypt, maak je gebruik van Console.ReadLine. Zorg zelf voor de juiste ondersteunende code.

Testscenario's

  • Test uit met een héél lang stuk tekst (meer dan 200 tekens) voor je favoriete kleur.

  • Test uit met tekst met internationale karakters, bijvoorbeeld de ç.

  • Ga na wat er gebeurt als je een lege regel invoert, dus als je meteen op ENTER duwt wanneer gevraagd wordt om invoer.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H0-gekleurde-rommelzin

Leerdoelen

  • de kleur van tekst in de console aanpassen

  • herhaling van de leerdoelen uit H0-rommelzin

Functionele analyse

Dit programma werkt net als H0-rommelzin, maar elke regel die aan de gebruiker wordt getoond, krijgt een andere kleur. De namen van de kleuren die je gebruikt (in deze volgorde) zijn:

  1. DarkGreen

  2. DarkRed

  3. DarkYellow

  4. Blue

  5. Cyan

  6. Red

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Technische hulp

Programmaverloop

Voor elke regel die in kleur getoond wordt, wissel je de voorgrondkleur. Op de juiste plaatsen in de code herstel je de oorspronkelijke kleuren van de terminal.

Testscenario's

  • Test opnieuw uit met een kleur, maaltijd, auto, film en boek naar keuze.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Kleuren in Console

Kleuren in console

Je kan in console-applicaties zelf bepalen in welke kleur nieuwe tekst op het scherm verschijnt. Je kan zowel de kleur van het lettertype instellen (via ForegroundColor) als de achtergrondkleur (BackgroundColor).

Je kan met de volgende expressies de console-kleur veranderen, bijvoorbeeld de achtergrond in blauw en de letters in groen:

Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.Green;

Vanaf dan zal alle tekst die je na deze 2 expressies via WriteLine naar het scherm stuurt met deze kleuren werken.

Een voorbeeld:

Console.WriteLine("Tekst in de standaard kleur");
Console.BackgroundColor = ConsoleColor.Blue;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Deze tekst komt in het groen met blauwe achtergrond");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("En deze in het rood met blauwe achtergrond");

Als je deze code uitvoert krijg je als resultaat:

Kleur in console gebruiken is nuttig om je gebruikers een minder eentonig en meer informatieve applicatie aan te bieden. Je zou bijvoorbeeld alle foutmeldingen in het rood kunnen laten verschijnen.

Kleur resetten

Soms wil je terug de originele applicatie-kleuren hebben. Je zou manueel dit kunnen instellen, maar wat als de gebruiker slecht ziend is en in z'n besturingssysteem andere kleuren als standaard heeft ingesteld?!

De veiligste manier is daarom de kleuren te resetten door de Console.ResetColor() methode aan te roepen zoals volgend voorbeeld toont:

Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Error!!!! Contacteer de helpdesk");
Console.ResetColor();
Console.WriteLine("Het programma sluit nu af");

Mogelijke kleuren

Alle kleuren die beschikbaar zijn zijn beschreven in ConsoleColor deze zijn:

  • ConsoleColor.Black

  • ConsoleColor.DarkBlue

  • ConsoleColor.DarkGreen

  • ConsoleColor.DarkCyan

  • ConsoleColor.DarkRed

  • ConsoleColor.DarkMagenta

  • ConsoleColor.DarkYellow

  • ConsoleColor.Gray

  • ConsoleColor.DarkGray

  • ConsoleColor.Blue

  • ConsoleColor.Green

  • ConsoleColor.Cyan

  • ConsoleColor.Red

  • ConsoleColor.Magenta

  • ConsoleColor.Yellow

Mee helpen?

De essentie van C#

Statements en de C# syntax

Om een werkend C#-programma te maken moeten we de C#-taal beheersen. Net zoals iedere taal, bestaat ook C# uit enerzijds grammatica, in de vorm van de C# syntax en anderzijds vocabulair in de vorm van de te gebruiken keywords.

Een C#-programma bestaat uit een opeenvolging van instructies ook wel statements genoemd. Deze eindigen steeds met een puntkomma (;) (zoals ook in het Nederlands een zin eindigt met een punt).

De volgorde van de woorden (keywords, variabelen, etc.) zijn niet vrijblijvend en moeten aan (grammaticale) regels voldoen. Enkel indien alle statements correct zijn zal het programma gecompileerd worden naar een werkend en uitvoerbaar programma (zoals in een vorige sectie besproken).

Enkele belangrijke regels van C#:

  • Hoofdletter-gevoelig: C# is hoofdlettergevoelig. Dat wil zeggen dat hoofdletter T en kleine letter t totaal verschillende zaken zijn voor C#. Reinhardt en reinhardt zijn dus ook niet hetzelfde.

  • Statements afsluiten met puntkomma: Iedere C# statement wordt afgesloten moet een puntkomma ( ; ). Doe je dat niet dan zal C# denken dat de regel gewoon op de volgende lijn doorloopt en deze als één (fout) geheel proberen te compileren.

  • Witruimtes: Spaties, tabs en enters worden door de C# compiler genegeerd. Je kan ze dus gebruiken om de layout van je code (bladspiegel zeg maar) te verbeteren. De enige plek waar witruimtes wél een verschil geven is tussen aanhalingstekens " " die we later (bij string) zullen leren gebruiken.

  • Commentaar toevoegen kan: met behulp van // voor een enkele lijn en /* */ voor meerdere lijnen commentaar. Alles dat in commentaar staat zal door de compiler genegeerd worden.

Keywords: de woordenschat

C# bestaat zoals gezegd niet enkel uit grammaticale regels. Grammatica zonder woordenschat is nutteloos. Er zijn binnen C# dan ook 80 woorden, zogenaamde reserved keywords die de woordenschat voorstellen. In deze cursus zullen we stelselmatig deze keywords leren kennen en gebruiken op een correcte manier om zo werkende code te maken.

Deze keywords zijn:

abstract

as

base

bool

break

byte

case

catch

char

checked

class

const

continue

decimal

default

delegate

do

double

else

enum

event

explicit

extern

false

finally

fixed

float

for

foreach

goto

if

implicit

in

int

interface

internal

is

lock

long

namespace

new

null

object

operator

out

override

params

private

protected

public

readonly

ref

return

sbyte

sealed

short

sizeof

stackalloc

static

string

struct

switch

this

throw

true

try

typeof

uint

ulong

unchecked

unsafe

ushort

using

using static

virtual

void

volatile

while

De keywords in vet zijn keywords die we dit semester zullen kennen. Die in cursief in het tweede semester. De overige zal je zelf moeten leren ;).

Indien je deze tabel in pdf bekijkt zal deze om zeep zijn. Onze gitbook gnomes proberen dit op te lossen maar voorlopig vinden ze helaas geen oplossing, waarvoor onze excuses.

Variabelen, identifiers en naamgeving

We hebben variabelen nodig om (tijdelijke) data in op te slaan. Wanneer we een statement schrijven dat bijvoorbeeld input van de gebruiker moet vragen, dan willen we ook die input bewaren zodat we verderop in het programma (het algoritme) iets met deze data kunnen doen. We doen hetzelfde in ons hoofd wanneer we bijvoorbeeld zegen "tel 3 en 4 op en vermenigvuldig dat resultaat met 5". Eerst zullen we het resultaat van 3+4 in een variabele moeten bewaren. Vervolgens zullen we de inhoud van die variabele vermenigvuldigen met 5 en dat nieuwe resultaat ook in een nieuwe variabele opslaan (om vervolgens bijvoorbeeld naar het scherm te sturen).

Wanneer we een variabele aanmaken zal deze moeten voldoen aan enkele afspraken. Zo moeten we minstens 2 zaken meegeven:

  • Het type van de variabele: het datatype dat aangeeft wat voor data we wensen op te slaan (tekst, getal, afbeelding, etc.).

  • De naam van de variabele: de identifier waarmee we snel aan de variabele-waarde kunnen.

Regels voor identifiers

De code die we gaan schrijven moet voldoen aan een hoop regels. Wanneer we in onze code zelf namen (identifiers) moeten geven aan variabelen (en later ook methoden, objecten, etc.) dan moeten we een aantal regels volgen:

  • Hoofdlettergevoelig: de identifiers tim en Tim zijn verschillend zoals reeds vermeld.

  • Geen keywords: identifiers mogen geen gereserveerde C# keywords zijn. De keywords van hierboven mogen dus niet. Varianten waarbij de hoofdletters anders zijn mogen wel, bijvoorbeeld: gOTO en stRINg mogen dus wel, maar niet goto of string daar beide een gereserveerd keyword zijn maar dankzij de hoofdlettergevoelig-regel is dit dus toegelaten. INT mag ook ook, maar niet int.

  • Eerste karakter-regel: het eerste karakter van de identifier mag enkel zijn:

    • kleine of grote letter

    • liggend streepje (_)

  • Alle andere karakters-regels: de overige karakters mogen enkel zijn:

    • kleine of grote letter

    • liggend streepje

    • een cijfer (0 tot en met 9)

  • Lengte: Een legale identifier mag zo lang zijn als je wenst, maar je houd het best leesbaar.

Enkele voorbeelden

Enkele voorbeelden van toegelaten en niet toegelaten identifiers:

identifier

toegelaten?

uitleg indien niet toegelaten

werknemer

ja

kerst2018

ja

pippo de clown

neen

geen spaties toegestaan

4dPlaats

neen

mag niet starten met getal

_ILOVE2019

ja

Tor+Bjorn

neen

enkel cijfers, letters en liggende streepjes toegestaan

ALLCAPSMAN

ja

B_A_L

ja

class

neen

gereserveerd keyword

WriteLine

ja

Naamgeving afspraken

  • Duidelijke naam: de identifier moet duidelijk maken waarvoor de identifier dient. Schrijf dus liever gewicht of leeftijd in plaats van a of meuh.

  • Camel casing: gebruik camel casing indien je meerdere woorden in je identifier wenst te gebruiken. Camel casing wil zeggen dat ieder nieuw woord terug met een hoofdletter begint. Een goed voorbeeld kan dus zijn leeftijdTimDams of aantalLeerlingenKlas1EA. Merk op dat we liefst het eerste woord met kleine letter starten. Uiteraard zijn er geen spaties toegelaten.

Commentaar

Soms wil je misschien extra commentaar bij je code zetten. Als je dat gewoon zou doen (bv Dit deel zal alles verwijderen) dan zal je compiler niet begrijpen wat die zin doet. Hij verwacht namelijk C# en niet een Nederlandstalige zin. Om dit op te lossen kan je in je code op twee manieren aangeven dat een stuk tekst gewoon commentaar is en mag genegeerd worden door de compiler:

Enkele lijn commentaar

Eén lijn commentaar geef je aan door de lijn te starten met twee voorwaartse slashes //. Uiteraard mag je ook meerdere lijnen op deze manier in commentaar zetten. Zo wordt dit ook vaak gebruikt om tijdelijk een stuk code "uit te schakelen". Ook mogen we commentaar achter een stuk C# code plaatsen (zie voorbeeld hieronder).

//De start van het programma
int getal=3;
//Nu gaan we rekenen
int result = getal * 5;
// result= 3*5;
Console.WriteLine(result); //We tonen resultaat op scherm: 15

Blok commentaar

We kunnen een stuk tekst als commentaar aangeven door voor de tekst /* te plaatsen en */ achteraan. Een voorbeeld:

/*
    Veel commentaar.
    Een heel verhaal
    Mooi he.
    Is dit een haiku?
*/
int leeftijd= 0;
leeftijd++;

Je eerste programma

Console-applicaties

Een console-applicatie is een programma dat zijn in- en uitvoer via een klassiek commando/shell-scherm toont. Een console-applicatie draait in dezelfde omgeving als wanneer we in Windows een command-prompt openen (via Start-> Uitvoeren-> cmd [enter] ). We zullen in deze cursus enkel console-applicaties leren maken. Grafische frontends (bv WPF) komen in deze cursus niet aan bod.

ReadLine en WriteLine

Console-applicaties maken in C# vereist dat je minstens twee belangrijke C# methoden leert gebruiken: Via Console.WriteLine() kunnen we tekst op het scherm tonen en met behulp van Console.ReadLine() kunnen we input van de gebruiker inlezen en in ons programma verwerken.

Je eerste console programma

Maak een nieuw console-project aan (noem het Demo1) en open het Program.cs bestand (indien het nog niet open is). Veeg de code die hier reeds staat niet weg!

Voeg onder de lijn Console.WriteLine("Hello World!"); volgende code toe:

Console.WriteLine("Hoi, ik ben het!");

Zodat je dus volgende code krijgt:

using System;

namespace Demo1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine("Hoi, ik ben het");
        }
    }
}

Compileer deze code en voer ze uit: **druk hiervoor op het groene driehoekje. Of via het menu Debug en dan Start Debugging.

Let erop dat je iedere 'zin' eindigt met een puntkomma.

Moet ik niets bewaren?

Neen. Telkens je op de groene "build en run" knop duwt worden al je aanpassingen bewaard. Trouwens: Kies nooit voor "save as..."!!!!

Dit zal aardig wat problemen in je project veroorzaken, geloof me maar.

Analyse van de code

We gaan nu iedere lijn code kort bespreken. Sommige lijnen code zullen lange tijd niet belangrijk zijn. Onthoud nu alvast dat: alle belangrijke code staat tussen de accolades onder de lijn static void Main(string[] args)!

  • using System; : Alle Console-commando's die we verderop gebruiken zitten in de System bibliotheek. Als we deze lijn (een zogenaamde directive) niet zouden schrijven dan moesten we System.Console.WriteLine i.p.v. Console.WriteLine schrijven verderop in de code.

  • namespace Demo1: Dit is de unieke naam waarbinnen we ons programma zullen steken, en het is niet toevallig de naam van je project. Verander dit nooit tenzij je weet wat je aan het doen bent.

  • class Program{}: Hier start je echte programma. Alle code binnen deze Program accolades zullen gecompileerd worden naar een uitvoerbaar bestand.

  • static void Main(string[] args): Het startpunt van iedere console-applicatie. Wat hier gemaakt wordt is een methode genaamd Main. Je programma kan meerdere methoden (of functies) bevatten, maar enkel degene genaamd Main zal door de compiler als het startpunt van het programma gemaakt worden.

  • Console.WriteLine("Hello world");: Dit is een statement dat de WriteLine-methode aanroept van de Console-klasse. Het zal alle tekst die tussen de aanhalingstekens staat op het scherm tonen.

  • Console.WriteLine("Hoi ik ben het");: en ook deze lijn komt op het scherm.

  • Sluitende accolades: vervolgens moet voor iedere openende accolade eerder in de code nu ook een bijhorende sluitende volgen.

Say what now?!

Oh boy...Wat was dit allemaal?! We hebben al aardig wat vreemde code zien passeren en het is niet meer dan normaal dat je nu denkt "dit ga ik nooit kunnen". Wees echter niet bevreesd: je zal sneller dan je denkt bovenstaande code als 'kinderspel' gaan bekijken. Een tip nodig? Test en experimenteer met wat je al kunt!

Laat deze info rustig inzinken en onthoudt alvast volgende belangrijke zaken:

  • Al je code komt binnen de Main accolades.

  • Eindig iedere lijn code daar met een puntkomma ( ; ).

WriteLine: Tekst op het scherm

De WriteLine-methode is een veelgebruikte methode in Console-applicaties. Het zorgt ervoor dat we tekst op het scherm kunnen tonen.

Voeg volgende lijn toe na de vorige WriteLine-lijn in je project:

Console.WriteLine("Wie ben jij?!");

De WriteLine methode zal alle tekst tonen die tussen de " " staan tussen de haakjes van de methode. De aanhalingstekens aan het begin en einde van de tekst zijn uiterst belangrijk! Alsook het puntkomma helemaal achteraan.

Je programma is nu:

using System;

namespace Demo1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine("Hoi, ik ben het");
            Console.WriteLine("Wie ben jij?!");
        }
    }
}

Kan je ook deze code uitvoeren?! Make it happen en ga dan gezwind naar het volgende hoofdstuk!

Afspraken code

Voor de graduaatsopleiding volgen we een reeks afspraken (ook "conventies" genoemd) die bepalen welke code wel en niet aanvaardbaar is. Wanneer een groep programmeurs dezelfde afspraken volgt, is het voor iedereen makkelijker code uit te wisselen.

Als je deze pagina voor de eerste keer ziet, zullen de meeste van deze afspraken je nog niet veel zeggen. Dat is logisch. We zullen je tijdens de les wijzen op de afspraken die horen bij een nieuw concept, maar hier heb je een handig overzicht:

naamgeving

Soort data

Notatie

Enkelvoud?

Vorm

Default zichtbaarheid

Klasse

PascalCase

altijd

[A-z]+[0-9]

public

Methode

PascalCase

meervoud toegestaan

[A-z]+[0-9]

public

Argument

camelCase

meervoud toegestaan

[A-z]+[0-9]

niet van toepassing

Lokale variabele

camelCase

meervoud toegestaan

[A-z]+[0-9]

niet van toepassing

Constante

PascalCase

altijd

[A-z]+[0-9]

public

Veld

camelCase

meervoud toegestaan

[A-z]+[0-9]

private

Property

PascalCase

meervoud toegestaan

[A-z]+[0-9]

public

Enum type

PascalCase

altijd

[A-z]+

public

Waarde enum

PascalCase

altijd

[A-z]+

niet van toepassing

  • We kiezen namen in het Engels. Enkel tekst die aan de gebruiker getoond wordt, schrijven we in het Nederlands.

  • Merk op dat het verschil tussen PascalCase en camelCase samenhangt met de default zichtbaarheid. Wanneer afgeweken wordt van de default zichtbaarheid, kan de conventie voor de notatie ook veranderen. Wij zullen ons in deze cursussen voor de eenvoud strikt aan de default zichtbaarheid houden.

  • "meervoud toegestaan" betekent dat we iets noteren als een meervoud als de data ook meerdere objecten voorstelt. Bijvoorbeeld een lokale variabele met naam studenten en datatype Student[], maar een lokale variabele student met datatype Student.

  • We beginnen niet elke identifier voor een private member met een underscore. Sommige programmeurs doen dit wel, maar wij volgen één richtlijn.

  • Namen van klassen voor custom exceptions eindigen op Exception.

  • Vermijd afkortingen in namen.

    • Uitzondering: algemeen aanvaarde afkortingen zoals ID, HTML,...

    • Afkortingen van één of twee letters

  • Andere talen hebben soms andere conventies!

codeerstijl

using directieven

  • Deze komen alleen vooraan in het bestand voor en worden steeds gevolgd door de declaratie van de namespace.

namespaces

  • In het eerste semester groeperen we al onze code in de namespace Programmeren, in het tweede semester in de namespace ObjectgerichtProgrammeren.

een datatype per bestand

  • Elk zelf gedefinieerd datatype (klasse, struct, interface, enum type, later ook delegate) plaatsen we in een afzonderlijk bestand met dezelfde naam als dat datatype.

strings bouwen

  • In de eerste twee lessen mag dit met behulp van +.

  • Gebruik stringinterpolatie om kleine aantallen strings aan elkaar te hangen of data weer te geven in stringformaat.

  • Gebruik een StringBuilder om strings aan elkaar te hangen in een lus.

types declareren

  • Gebruik steeds een expliciet, statisch type.

    • Dat wil zeggen: geen variabelen declareren als var. Dat doe je pas later.

    • Dat wil zeggen: geen variabelen declareren als dynamic. Dat doe je pas later.

arrays

  • Gebruik de kortste syntax voor arrays van literals, dus string[] vowels1 = { "a", "e", "i", "o", "u" }; en niet string[] vowels2 = new string[] { "a", "e", "i", "o", "u" };.

static members

  • Plaats voor een static member altijd uitdrukkelijk de naam van de klasse waarin dat static member gedefinieerd is.

  • Klassen die uitsluitend static members bevatten ("library classes") maken we ook static met de syntax static class.

vergrendelen van data

  • Klassen die niet bedoeld zijn om van over te erven maken we final.

  • Als iets een constante is voor een bepaald programme, gebruiken we ook de access modifier const.

algemeen

We gebruiken alleen zaken die in de les aan bod zijn gekomen. We zien geen lambda's, delegates, LINQ,... dus je gebruikt deze ook niet, zelfs als je ze al ergens anders gezien hebt.

Variabelen

Variabelen

In het vorige hoofdstuk zagen we dat er verschillende soorten datatypes bestaan. Deze types hebben we nodig om variabelen aan te maken. De data die we in een programma gebruiken bewaren we namelijk in een variabele van een bepaald type. Een variabele is een plekje in het geheugen dat in je programma zal gereserveerd worden om daarin data te bewaren van het type dat je aan de variabele hebt toegekend. Een variabele zal intern een geheugenadres hebben (waar de data in het geheugen staat) maar dat zou lastig programmeren zijn indien je steeds dit adres moet gebruiken. Daarom moeten we ook steeds een naam oftewel identifier aan de variabele geven zodat we makkelijk de geheugenplek kunnen aanduiden.

De naam (identifier) van de variabele moet voldoen aan de identifier regels onder "Inleiding -> Afspraken code".

Variabelen aanmaken en gebruiken

Om een variabele te maken moeten we deze declareren, door een type en naam te geven. Vanaf dan zal de computer een hoeveelheid geheugen voor je reserveren waar de inhoud van deze variabele in kan bewaard worden. Hiervoor dien je minstens op te geven:

  1. Het datatype (bv int, double).

  2. (optioneel) Een beginwaarde die de variabele krijgt bij het aanmaken ervan.

Een variabele declaratie heeft als syntax:

Bijvoorbeeld: int leeftijd;

Je mag ook meerdere variabelen van het zelfde datatype in 1 enkele declaratie aanmaken door deze met komma's te scheiden:

Bijvoorbeeld string voornaam, achternaam, adres;

Indien je reeds weet wat de beginwaarde moet zijn van de variabele dan mag je de variabele ook reeds deze waarde toekennen bij het aanmaken:

Waarden toekennen aan variabelen

Vanaf dit punt kunnen we dus ten allen tijde deze variabele gebruiken om een waarde aan toe te kennen, de bestaande waarde te overschrijven, of de waarde te gebruiken, zoals:

  • Waarde toekennen: mijnGetal= 15;. Toekenning gebeurt steeds van rechts naar links: het deel rechts van het gelijkheidsteken wordt toegewezen aan het deel links er van.

  • Waarde tonen op scherm: Console.WriteLine(mijnGetal);

Met de toekennings-operator (=) kan je een waarde toekennen aan een variabele. Hierbij kan je zowel een literal toekennen oftewel het resultaat van een expressie.

Je kan natuurlijk ook een waarde uit een variabele uitlezen en toewijzen aan een andere variabele:

Literal toewijzen

Literals zijn expliciet ingevoerde waarden in je code. Als je in je code expliciet de waarde 4 wilt toekennen aan een variabele dan is het getal 4 in je code een zogenaamde literal. Wanneer we echter data bijvoorbeeld eerst uitlezen of berekenen (via bijvoorbeeld invoer van de gebruiker of als resultaat van een berekening) en het resultaat hiervan toekennen aan een variabele dan is dit geen literal.

Voorbeelden van een literal toekennen:

Het is belangrijk dat het type van de literal overeenstemt met dat van de variabele waaraan je deze zal toewijzen. Een string-literal (zie verder) stel je voor door aanhalingstekens. Volgende code zal dan ook een compiler-fout generen, daar je een string-literal aan een int-variabele wil toewijzen, en vice versa.

Als je bovenstaande probeert te compileren dan krijg je volgende error-boodschappen:

Literal bepaald het datatype

De manier waarop je een literal schrijft in je code zal bepalen wat het datatype van de literal is:

  • Gehele getallen worden standaard als int beschouwd, vb: 125.

  • Kommagetallen (met punt .) worden standaard als double beschouwd, vb: 12.5.

  • Via een suffix na het getal kan je aangeven als het om andere types gaat:

    • U of u voor uint, vb: 125U.

    • L of l voor long, vb: 125L.

    • UL of ul voor ulong, vb: 125ul.

    • F of f voor float, vb: 12.5f.

    • M of m voor decimal, vb: 12.5M.

  • Voor bool (zie verder) is dit enkel true of false.

  • Voor char (zie verder) wordt dit aangeduid met een enkele apostrof voor en na de literal, vb: 'q'.

  • Voor string (zie verder) wordt dit aangeduid met aanhalingsteken voor en na de literal, vb: "pikachu".

De overige types sbyte, short en ushort hebben geen literal aanduiding. Er wordt vanuit gegaan wanneer je een literal probeert toe te wijzen aan een van deze types dat dit zonder problemen zal gaan (ze worden impliciet geconverteerd).

Hexadecimale en binaire notatie

Je kan ook hexadecimale notatie (starten met 0x of 0X) gebruiken wanneer je bijvoorbeeld met int of byte werkt:

Ook binaire notatie (starten met 0b of 0B) kan:

Nieuwe waarden overschrijven oude waarden

Wanneer je een reeds gedeclareerde variabele een nieuwe waarde toekent dan zal de oude waarde in die variabele onherroepelijk verloren zijn. Probeer dus altijd goed op te letten of je de oude waarde nog nodig hebt of niet. Wil je de oude waarde ook nog bewaren dan zal je een nieuwe, extra variabele moeten aanmaken en daarin de nieuwe waarde moeten bewaren:

In dit voorbeeld zal er dus voor gezorgd worden dat de oude waarde van temperatuurGisteren, 20, overschreven zal worden met 25.

Volgende code toont hoe je bijvoorbeeld eerst de vorige waarde kunt bewaren en dan overschrijven:

We hebben dus aan het einde van het programma zowel de temperatuur van eergisteren, 20, als die van vandaag, 25.

Kennisclip

Strings samenvoegen

Strings samenvoegen

Je kan strings en variabelen samenvoegen tot een nieuwe string op verschillende manieren:

  • +-operator

  • $ string interpolation

  • Of de oude manier: String.Format()

In deze cursus prefereren we manier 2, de string interpolatie. Dit is de meest moderne aanpak.

In dit hoofdstuk

We gaan van volgende informatie uit:

  • Stel dat je 2 variabelen hebt int age=13 en string name="Finkelstein".

  • We willen de inhoud van deze variabelen samenvoegen in een nieuwe string result die zal bestaan uit de tekst:

    Ik ben Finkelstein en ik ben 13 jaar oud.

Volgende 3 manieren tonen hoe je steeds tot voorgaande string zal komen.

Manier 1: String samenvoegen met de +-operator

Je kan string en variabelen eenvoudig bij elkaar 'optellen'. Ze worden dan achter elkaar geplakt als het ware.

Let er op dat je tussen de aanhalingsteken (binnen de strings) spaties zet indien je het volgende deel niet tegen het vorige stringstuk wilt 'plakken'.

Manier 2: String interpolation met $

In de oude dagen van C# gebruikten we String.Format() (zie hieronder) om meerdere strings en variabelen samen te voegen tot één string. Nu kan dat met string interpolation waarbij we het $-teken gebruiken.

Door het $-teken VOOR de string te plaatsen geef je aan dat alle delen in de string die tussen accolades staan { } als code mogen beschouwd worden. Een voorbeeld maakt dit duidelijk:

In dit geval zal dus de inhoud van de variabele name tussen de string op de plek waar nu {name} staat geplaatst worden. Idem voor age. Zoals je kan zien is dit veel meer leesbare code dan de eerste manier.

Berekeningen doen bij string interpolatie

Je mag eender welke expressie tussen de accolades zetten bij string interpolation, denk maar aan:

Alle expressies tussen de accolades zullen eerst uitgevoerd worden voor ze tussen de string worden geplaatst. De uitvoer wordt nu dus: Ik ben Finkelstein en ik ben 17 jaar oud.

Eender welke expressie is toegelaten, dus je kan ook complexe berekeningen of zelfs andere methoden aanroepen:

Mooier formatteren

Zowel bij manier string interpolation (manier 2) als de manier hierna kan je ook bepalen hoe de te tonen variabelen en expressies juist weergegeven moeten worden. Je geeft dit aan door na de expressie, binnen de accolades, een dubbelpunt te plaatsen gevolgd door de manier waarop moet geformatteerd worden:

Wil je bijvoorbeeld een kommagetal tonen met maar 2 cijfers na de komma dan schrijf je:

Nog enkele nuttige vormen:

  • D5: geheel getal bestaande uit 5 cijfers (123 wordt 00123) (werkt enkel op gehele getallen!)

  • E2: wetenschappelijke notatie met 2 cijfers precisie (12000000 wordt 1,20E+007)

  • C: geldbedrag (12,34 wordt € 12,34: teken van valuta afhankelijk van instellingen pc)

Manier 3: String.Format()

String interpolatie met het $-teken is een nieuwe C# aanwinst. Je zal echter nog veel documentatie en online code tegenkomen die nog met String.Format werkt (ook zijn er nog zaken waar het te prefereren is om String.Format te gebruiken i.p.v. 1 van vorige manieren). Om die reden bespreken we dit nog in deze cursus.

String.Format is een ingebouwde methode die string-interpolatie toe laat op een iets minder intuïtieve manier:

Het getal tussen de accolades geeft aan de hoeveelste parameter na de string hier in de plaats moet gezet worden (0= de eerste, 1= de tweede, enz).

Volgende code zal een ander resultaat geven:

Namelijk: Ik ben 13 en ik ben 13 jaar oud.

Zie je de fout?
De errorlist
Lampje
De eerste regel behoort niet tot het programma. De rest moet er bij jou hetzelfde uitzien.
Cheat sheet preview
Compiler process
VS2019 Logo
Vereenvoudigd compiler overzicht
VS Opstarten
VS Project maken
VS Project maken
VS Ide Overzicht
VS Layout resetten
Command shell
Command shell
Resultaat voorgaande code
Bron afbeelding : https://www.c-sharpcorner.com/article/change-console-foreground-and-background-color-in-c-sharp/

Als je aanpassingen wil voorstellen specifiek voor de graduaatsopleiding, dan wordt aangeraden een fork van deze repository te maken op Github. De verschillen tussen de cursus voor de graduaatsopleiding en de bachelor hebben bijna uitsluitend betrekking tot de oefeningen. Als je aanpassingen wil doen met betrekking tot de inhoud, zie dan .

De verschillende datatypes bespreken we in een volgend .

Er zijn geen vaste afspraken over hoe je je variabelen moet noemen toch hanteren we enkele coding guidelines die doorheen je opleiding moeten gevolgd worden. Naarmate we meer C# leren zullen er extra guidelines bijkomen (zie ).

Onderstaande richtlijnen zijn gebaseerd op . We beperken ons tot de zaken die we tijdens de cursus zien.

We baseren ons op . Je hoeft deze conventies niet te lezen. Hieronder volgt een vereenvoudigd overzicht van de afspraken die aan bod kunnen komen in de cursus en enkele eigen conventies:

Een identifier zodat de variabele uniek kan geïdentificeerd worden ().

Alle format specifiers staan .

Wens je meer informatie over string.Format, kijk dan .

de overeenkomstige pagina in de cursus van de bachelor
hoofdstuk
deze pagina met richtlijnen
deze algemeen aanvaarde richtlijnen
de afspraken van Microsoft
datatype identifier;
datatype identifier1, identifier2, identifier3;
int mijnLeeftijd = 37;
int eenAndereLeeftijd = mijnLeeftijd;
int temperatuurGisteren = 20;
int temperatuurVandaag = 25;
string eenTekst;
int eenGetal;

eenTekst = 4;
eenGetal = "4";
int myAge= 0x0024;
byte myByteValue = 0x00C9;
int myAge= 0b00010110001101000010‬; //Vanaf C# 7.2 mag je ook schrijven: 0b0001_0110_0011_0100_0010
byte myByteValue =  0b‭00100100‬9;
int temperatuurGisteren = 20;
temperatuurGisteren = 25;
int temperatuurGisteren= 20;
//Doe vanalles
//...
//1 dag later
int temperatuurEerGisteren= temperatuurGisteren; //Vorige temperatuur in eergisteren bewaren
temperatuurGisteren = 25; //temperatuur nu overschrijven
string result= "Ik ben "+ name + " en ik ben "+ age+ " jaar oud.";
string result= $"Ik ben {name} en ik ben {age} jaar oud.";
string result= $"Ik ben {name} en ik ben {age+4} jaar oud.";
string result= $"Ik ben {age*age+(3%2)} jaar oud.";
double number = 12.345;
Console.WriteLine($"{number:F2}");
string result= String.Format("Ik ben {0} en ik ben {1} jaar oud.",name,age);
string result= String.Format("Ik ben {1} en ik ben {1} jaar oud.",name,age);
volgens de naamgevingsregel van C#
Variabelen en datatypes
hier opgelijst
hier

Input verwerken met ReadLine

We nemen terug ons eerste programma erbij en gaan hier aan verder werken:

ReadLine: Input van de gebruiker verwerken

Met de console kan je met een handvol methoden reeds een aantal interessante dingen doen.

Zo kan je bijvoorbeeld input van de gebruiker inlezen en bewaren in een variabele als volgt:

Bespreking van deze code:

string result;

  • Concreet zeggen we hiermee aan de compiler: maak in het geheugen een plekje vrij waar enkel data van het type string in mag bewaard worden;

  • Noem deze geheugenplek result zodat we deze later makkelijk kunnen in en uitlezen.

result = Console.ReadLine();

  • Vervolgens roepen we de ReadLine methode aan. Deze methode zal de invoer van de gebruiker uitlezen tot de gebruiker op enter drukt.

  • Het resultaat van de ingevoerde tekst wordt bewaard in de variabele result (denk eraan dat de toekenning van rechts naar links gebeurt).

Je programma zou nu moeten zijn:

Start nogmaals je programma. Je zal merken dat je programma nu een cursor toont en wacht op invoer. Je kan nu eender wat intypen en van zodra je op enter duwt gaat het programma verder (in dit geval stopt het programma hierna dus).

Input gebruiker verwerken en gebruiken

We kunnen nu invoer van de gebruiker, die we hebben bewaard in de variabele result gebruiken en tonen op het scherm.

Op de tweede lijn hier gebruiken we de variabele result (waar de invoer van de gebruiker in bewaard wordt) als parameter in de WriteLine-methode.

Met andere woorden: de WriteLine methode zal op het scherm tonen wat de gebruiker even daarvoor heeft ingevoerd.

Je volledige programma ziet er dus nu zo uit:

Test het programma en voer je naam in wanneer de cursor knippert.

Voorbeelduitvoer (lijn 3 is wat de gebruiker heeft ingetypt)

Aanhalingsteken of niet?

Wanneer je de inhoud van een variabele wil gebruiken in een methode zoals WriteLine() dan plaats je deze zonder aanhalingsteken! Bekijk zelf eens wat het verschil wordt wanneer je volgende lijn code Console.Write(result); vervangt door Console.Write("result");.

De uitvoer wordt dan:

Write en WriteLine

De WriteLine-methode zal steeds een line break (een 'enter' ) aan het einde van de lijn zetten zodat de cursor naar de volgende lijn springt.

De Write-methode zal geen enter aan het einde van de lijn toevoegen. Als je dus vervolgens iets toevoegt (met een volgende Write of WriteLine) dan zal dit aan dezelfde lijn toegevoegd worden.

Vervang daarom eens de laatste 3 lijnen code in je project door:

Voer je programma uit en test het resultaat. Je krijgt nu:

Wat is er verkeerd gelopen? Al je tekst van de laatste lijn plakt zo dicht bij elkaar? Inderdaad, we zijn spaties vergeten toe te voegen! Spaties zijn ook tekens die op scherm moeten komen (ook al zien we ze niet) en je dient dus binnen de aanhalingstekens spaties toe te voegen. Namelijk:

Je uitvoer wordt nu:

Opletten met spaties

Spaties zijn ook tekens die op scherm moeten komen (ook al zien we ze niet) en je dient dus binnen de aanhalingstekens spaties toe te voegen. Indien je deze erbuiten plaats dan heeft dit geen effect (je wist al uit het eerste hoofdstuk dat C# alle witregels negeert die niet tussen aanhalingstekens staan). In volgend voorbeeld zijn de spaties aangegeven als liggende streepjes ( _ ).

Fout (de code zal werken maar je spaties worden genegeerd):

Correct:

Zinnen aan elkaar plakken

We kunnen dit hele verhaal een pak korter tonen. De plus-operator (+) in C# kan je namelijk gebruiken om variabelen van het type string aan elkaar te plakken. De laatste 3 lijnen code kunnen korter geschreven worden als volgt:

Merk op dat result dus NIET tussen aanhalingstekens staat, in tegenstelling de andere stukken zin. Waarom is dit? Aanhalingstekens in C# duiden aan dat een stuk tekst moet beschouwd worden als tekst van het type string. Als je geen aanhalingsteken gebruikt dan zal C# de tekst beschouwen als een variabele met die naam.

Bekijk zelf eens wat het verschil wanneer je volgende lijn code vervangt door de lijn er onder:

Meer input vragen

Als je meerdere inputs van de gebruiker tegelijkertijd wenst te bewaren dan zal je meerdere geheugenplekken nodig hebben om de invoer te bewaren. Bijvoorbeeld:

Je mag echter ook de geheugenplekken al vroeger maken. In C# zet men de geheugenplek creatie zo dicht mogelijk bij de code waar je die plek gebruikt (zoals vorig voorbeeld), maar dat is geen verplichting. Dit mag dus ook:

using System;

namespace Demo1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            Console.WriteLine("Hoi, ik ben het");
            Console.WriteLine("Wie ben jij?!");
        }
    }
}
string result;
result = Console.ReadLine();
namespace Demo1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hoi, ik ben het!");
            Console.WriteLine("Wie ben jij?!");
            string result;
            result = Console.ReadLine();
        }
    }
}
Console.WriteLine("Dag ");
Console.WriteLine(result);
Console.WriteLine(" hoe gaat het met je?");
using System;

namespace Demo1
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("Hoi, ik ben het!");
            Console.WriteLine("Wie ben jij?!");

            string result;
            result = Console.ReadLine();

            Console.WriteLine("Dag");
            Console.WriteLine(result);
            Console.WriteLine("hoe gaat het met je?");
        }
    }
}
Hoi, ik ben het!
Wie ben jij?!
tim [enter]
Dag
tim
hoe gaat het met je?
Hoi, ik ben het!
Wie ben jij?!
tim [enter]
Dag
result
hoe gaat het met je?
Console.Write("Dag");
Console.Write(result);
Console.Write("hoe gaat het met je?");
Hoi, ik ben het!
Wie ben jij?!
tim [enter]
Dagtimhoe gaat het met je?
Console.Write("Dag ");
Console.Write(result);
Console.Write(" hoe gaat het met je?");
Hoi, ik ben het!
Wie ben jij?!
tim [enter]
Dag tim hoe gaat het met je?
Console.Write("Dag"_);
Console.Write(result_);
Console.Write("hoe gaat het met je?");
Console.Write("Dag_");
Console.Write(result);
Console.Write("_hoe gaat het met je?");
Console.WriteLine("Dag " + result + " hoe gaat het met je?");
Console.WriteLine("Dag "+ result + " hoe gaat het met je?");
Console.Write("Dag "+ "result" + " hoe gaat het met je?");
Console.WriteLine("Geef leeftijd");
string leeftijd;  //eerste geheugenplekje aanmaken
leeftijd = Console.ReadLine();
Console.WriteLine("Geef adres");
string adres; //tweede geheugenplekje aanmaken
adres = Console.ReadLine();
string leeftijd;  //eerste geheugenplekje aanmaken
string adres; //tweede geheugenplekje aanmaken
Console.WriteLine("Geef leeftijd");
leeftijd = Console.ReadLine();
Console.WriteLine("Geef adres");
adres = Console.ReadLine();

Welkom

Zie scherp

Concreet is dit het handboek voor de eerste 2 semesters omtrent 'leren programmeren met C#':

  • Deel 1: Programming Principles, eerste semester

  • Deel 2: Object Oriented Programming, tweede semester

Veel leer-en leesplezier,

De lectoren programmeren

PS Besef dat goed kunnen programmeren enkel kan geleerd worden indien je ook effectief veel programmeert. Je kan ook niet leren fietsen door enkel een handboek "Fietsen met Merckx" te lezen, je zal op de fiets moeten springen! En vallen...véél vallen.

Licensed under Creative Commmons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)

You are free to

Share : copy and redistribute the material in any medium or format

Adapt : remix, transform, and build upon the material

The licensor cannot revoke these freedoms as long as you follow the license terms.

Under the following terms

Attribution : You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. NonCommercial : You may not use the material for commercial purposes.

No additional restrictions : You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

Strings en chars

Char

Een enkel karakter (cijfer, letter, leesteken, etc.) als 'tekst' opslaan kan je doen door het char-type te gebruiken. Zo kan je bijvoorbeeld een enkel karakter als volgt tonen:

String

Een string is een reeks van 0, 1 of meerdere char-elementen, zoals je ook kan zien als je even met je muis boven een string keyword hovert in je code:

Strings declareren

Merk op dat we bij een string literal gebruik maken van aanhalingstekens (") terwijl bij chars we een apostrof gebruiken ('). Dit is de manier om een string van een char te onderscheiden.

Volgende code geeft dus drie keer het cijfer 1 onder elkaar op het scherm, maar de eerste keer behelst het een char (enkelvoudig teken), dan een een string (reeks van tekens) en dan een int (effectief getal):

De output van dit programma zal dan zijn:

Fout gebruik van strings en chars zal code geven die niet zal gecompileerd worden:

  1. In de eerste toekenning proberen we dus een literal van het type string toe te kennen een variabele van het type char.

  2. In de tweede toekenning proberen we een literal van het type char toe te kennen een variabele van het type string.

  3. In de laatste toekenning proberen we een literal van het type char toe te kennen aan een variabele van het type int.

Deze cursus wordt gebruikt als handboek binnen de .

Deze cursus is gebaseerd op .

Licensed under CC-BY-NC 4.0 as shown .

This is a human-readable summary of (and not a substitute for) the .

Het is belangrijk dat je de apostrof (' ) niet vergeet voor en na het karakter dat je wenst op te slaan daar dit de literal voorstelling van char-literals is (zie ook ).

Je kan eender welk in een char bewaren, namelijk letters, cijfers en speciale tekens zoals %, $, *, #, etc. Merk dus op dat volgende lijn: char eenGetal = '7'; weliswaar een getal als teken opslaat, maar dat intern de compiler deze variabele steeds als een character zal gebruiken. Als je dit cijfer zou willen gebruiken als effectief cijfer om wiskundige bewerkingen op uit te voeren, dan zal je dit eerst moeten converteren naar een getal (zie ).

graduaatsopleiding programmeren van de AP Hogeschool
de overeenkomstige cursus van de bacheloropleiding toegepaste informatica
here
license
char eenLetter = 'X';
Console.WriteLine("eenLetter=" + eenLetter);
char eenKarakter = '1'; 
string eenString = "1"; 
int eenGetal = 1;

Console.WriteLine(eenKarakter);
Console.WriteLine(eenString);
Console.WriteLine(eenGetal);
1
1
1
char eenKarakter = "1"; //fout
string eenString = '1'; //fout
int eenGetal = '1'; //fout
hier
UNICODE-teken
Convert en Casting

Expressies en operators

Expressies en operators

Zonder expressies is programmeren saai: je kan dan enkel variabelen aan elkaar toewijzen. Expressies zijn als het ware eenvoudige tot complexe sequenties van bewerkingen die op 1 resultaat uitkomen. De volgende code is bijvoorbeeld een expressie: 3+2.

Het resultaat van deze expressie is 5.

Expressie-resultaat toewijzen

Meestal zal je expressies schrijven waarin je bewerkingen op en met variabelen uitvoert. Vervolgens zal je het resultaat van die expressie willen bewaren voor verder gebruik in je code.

Voorbeeld van expressie-resultaat toekennen:

int temperatuursVerschil = temperatuurGisteren - temperatuurVandaag;

Hierbij zal de temperatuur uit de rechtse 2 variabelen worden uitgelezen, van elkaar wordt afgetrokken en vervolgens bewaard worden in temperatuursVerschil.

De voorgaande code kan ook langer geschreven worden als:

int tussenResultaat = temperatuurGisteren - temperatuurVandaag;
int temperatuursVerschil = tussenResultaat;

Een ander voorbeeld van een expressie-resultaat toewijzen maar nu met literals (stel dat we temperatuursVerschil reeds hebben gedeclareerd eerder):

temperatuursVerschil = 21 - 25;

Uiteraard mag je ook combinaties van literals en variabelen gebruiken in je expressies:

int breedte = 15;
int hoogte = 20 * breedte;

Operators

Operators in C# zijn de welgekende 'wiskundige bewerkingen' zoals optellen (+), aftrekken (-), vermenigvuldigen (*) en delen (/). Deze volgen de wiskundige regels van volgorde van berekeningen:

  1. Haakjes

  2. Vermenigvuldigen, delen en modulo: * (vermenigvuldigen), / (delen) en % (rest na deling, ook module genoemd)

  3. Optellen en aftrekken: + en -

    (etc.)

Net zoals in de wiskunde kan je in C# met behulp van de haakjes verplichten het deel tussen de haakjes eerst te doen, ongeacht de andere operators en hun volgorde van berekeningen:

3+5*2 => zal 13 als resultaat geven
(3+5)*2 => zal 16 geven

Je kan nu complexe berekeningen doen door literals, operators en variabelen samen te voegen. Bijvoorbeeld om te weten hoeveel je op Mars zou wegen:

double gewichtOpAarde= 80.3;        //kg
double zwaartekrachtAarde= 9.81;    //m/s² 
double zwaartekrachtMars= 3.711;    //m/s²

double  gewichtOpMars= (gewichtOpAarde/zwaartekrachtAarde) * zwaartekrachtMars; //kg
Console.WriteLine("Je weegt op Mars" + gewichtOpMars + " kg");

Modulo operator %

De modulo operator die we in C# aanduiden met % verdient wat meer uitleg. Deze operator zal als resultaat de gehele rest teruggeven wanneer we het linkse getal door het rechtse getal delen:

7%2 => zal 1 geven, daar 7 gedeeld door 2,  3 met rest 1 geeft 
10%5 => zal 0 geven, daar 10 gedeeld door 5, 2 met rest 0 geeft

De modulo-operator zal je geregeld gebruiken om bijvoorbeeld te weten of een getal een veelvoud van iets is. Als de rest dan 0 is weet je dat het getal een veelvoud is van het getal waar je het door deelde.

Bijvoorbeeld om te testen of getal even is gebruiken we %2:

int getal= 1234234;
int rest= getal%2;
Console.WriteLine($"Indien het getal als rest 0 geeft weten we dat het even is. De rest is: {rest}");

Verkorte operator notaties

Heel vaak wil je de inhoud van een variabele bewerken en dan terug bewaren in de variabele zelf. Bijvoorbeeld een variabele vermenigvuldigen met 10 en het resultaat ervan terug in de variabele plaatsen. Hiervoor zijn enkele verkorte notaties in C#. Stel dat we een variabele int getal hebben:

Verkorte notatie

Lange notatie

Beschrijving

getal++;

getal= getal+1;

variabele met 1 verhogen

getal--;

getal= getal-1;

variabele met 1 verlagen

getal+=3;

getal= getal+3;

variabele verhogen met een getal

getal-=6;

getal= getal-6;

variabele verminderen met een getal

getal*=7;

getal= getal*7;

variabele vermenigvuldigen met een getal

getal/=2;

getal= getal/2;

variabele delen door een getal

Je zal deze verkorte notatie vaak tegenkomen. Ze zijn identiek aan elkaar en zullen dus je code niet versnellen. Ze zal enkel compacter zijn om te lezen. Bij twijfel, gebruik gewoon de lange notatie.

Expressiedatatypes

De types die je in je berekeningen gebruikt bepalen ook het type van het resultaat. Als je bijvoorbeeld twee int variabelen of literals optelt zal het resultaat terug een int geven.

int result= 3+4;

Je kan echter geen kommagetallen aan int toewijzen. Als je dus twee double variabelen deelt is het resultaat terug een double en zal deze lijn een fout geven daar je probeert een double aan een int toe te wijzen:

int otherResult= 3.1/45.2;

Let hier op!

But wait... it gets worse!

Wat als je een int door een int deelt? Het resultaat is terug een int. Je bent gewoon alle informatie na de komma kwijt. Kijk maar:

int getal1= 9;
int getal2= 2;
int result= getal1/getal2;
Console.WriteLine(result);

Er zal 4 op het scherm verschijnen! (niet 4.5 daar dat geen int is).

Wat als je datatypes mengt? Als je een berekening doet met een int en een double dan zal C# het 'grootste' datatype kiezen. In dit geval een double. Volgende code zal dus werken:

double result= 3/5.6;

Volgende niet:

int result= 3/5.6;

Wil je dus het probleem oplossen om 9 te delen door 2 dan zal je minstens 1 van de 2 literals of variabelen door een double moeten omzetten. Het voorbeeld van hierboven herschrijven we dan naar:

int getal1= 9;
double getal2= 2.0;
double result= getal1/getal2;
Console.WriteLine(result);

En nu krijgen we wel 4.5.

En complexer?

Het kan subtiel en ambetant worden in grotere berekeningen.

Stel dat ik afspreek dat je van mij de helft van m'n salaris krijgt. Ik verdien (fictief) 10000 euro per maand. Ik gebruik volgende formule:

double helft= 10000.0 * (1/2);

Hoeveel krijg je van me? 0.0 euro, MUHAHAHAHA!!!

De volgorde van berekeningen zal eerst het gedeelte tussen de haakjes doen: 1 delen door 2 geeft 0, daar we een int door een int delen en dus terug een int als resultaat krijgen. Vervolgens zullen we deze 0 vermenigvuldigen met 10000.0 waarvan ik zo slim was om deze in double te zetten. Niet dus. We vermenigvuldigen weliswaar een double (het salaris) met een int maar die int is reeds 0 en we krijgen dus 0.0 als resultaat.

Wil je het dus eerlijk spelen dan zal je de formule moeten aanpassen naar:

double helft = 10000.0 * (1.0/2);

Nu krijgt het gedeelte tussen de haakjes een double als resultaat, namelijk 0.5 dat we dan kunnen vermenigvuldigen met het salaris om 5000.0 te krijgen.

Kennisclip

Over expressies in C# is véél te vertellen, maar niet aan de orde in deze cursus. Bekijk indien je meer interesse hebt.

zeker volgende tekst
Expressies

Unicode tonen

Unicode tonen

Niets is zo leuk als de vreemdste tekens op het scherm tonen. In oude console-games werden deze tekens vaak gebruikt om complexe tekeningen op het scherm te tonen: om je filmpjes nog cooler te maken leggen we daarom uit hoe je dit kan doen, gebruikmakende van je kennis over converteren.

Unicode karakters tonen

Zonder een uitleg te geven over het verschil tussen ASCII en Unicode is het vooral belangrijk te weten dat je best met Unicode werkt.

  1. Plaats bovenaan je Main: Console.OutputEncoding = System.Text.Encoding.UTF8;

  2. Je kan nu op 2 manieren dit teken in console plaatsen

Manier 1: copy/paste

Kopieer het karakter zelf en plaats het in je code waar je het nodig hebt, bijvoorbeeld:

Console.WriteLine("˧");

Manier 2: hexadecimale code casten naar char

Noteer de hexadecimale code van het karakter dat in de tabel staat.

In dit geval is de code 0x02e7.

Om dit teken te tonen schrijf je dan:

char blokje = (char)0x02e7;
Console.WriteLine(blokje);

In C# schrijf je hexadecimale getallen als volgt als je ze rechstreeks in een string wilt plaatsen: \u02e7

Wil je dus bovenstaande teken schrijven dan kan dan ook als volgt:

Console.WriteLine("\u02e7");

Ascii-art tonen

Soms zou je multiline ASCII-art willen tonen in je C# applicatie. Dit kan je eenvoudig oplossen door gebruik te maken van het @ teken voor een string.

Stel dat je een toffe titel of tekening via een van volgende sites hebt gemaakt:

Je kan het resultaat eenvoudig naar je klembord kopiëren en vervolgens in je C#-code integraal copy pasten als literal voor een string op voorwaarde dat je het laat voorafgaan door @" en uiteraard eindigt met ";.

Bijvoorbeeld:

string myname=@"
___________________   
\__    ___/\______ \  
  |    |    |    |  \ 
  |    |    |    `   \
  |____|   /_______  /
                   \/ ";

Console.WriteLine(myname);
Dwarven fortress

Zoek het teken(s) dat je nodig hebt in een Unicode-tabel ()

deze is handig
Asciiflow.Com
Ascii title generator
Ascii generator

Random

Random getallen genereren

Random getallen genereren in je code kan leuk zijn om de gebruiker een interactievere ervaring te geven. Beeld je in dat je monsters steeds dezelfde weg zouden bewandelen of dat er steeds op hetzelfde tijdstip een orkaan op je stad neerdwaalt. SAAI!

Random generator

De Random-klasse laat je toe om eenvoudig willekeurige gehele en komma-getallen te maken. Je moet hiervoor twee zaken doen:

  1. Roep de Next methode aan telkens je een nieuw getal nodig hebt, bijvoorbeeld: int mijnGetal= randomgen.Next(); De aanroep van de methode Next() zal een geheel getal willekeurig genereren.

De eerste stap dien je dus maar 1 keer te doen. Vanaf dan kan je telkens aan de generator een nieuw getal vragen m.b.v. Next.

Volgende code toont bijvoorbeeld 3 random getallen op het scherm:

Random mygen= new Random();
int getal1= mygen.Next();
int getal2= mygen.Next();
int getal3= mygen.Next();
Console.WriteLine(getal1);
Console.WriteLine(getal2);
Console.WriteLine(getal3);

Next mogelijkheden

Je kan de Next methode ook 2 parameters meegeven, namelijk de grenzen waarbinnen het getal moet gegenereerd worden. De tweede parameter is exclusief dit getal zelf. Wil je dus een getal tot en met 10 dan schrijf je 11, niet 10.

Enkele voorbeelden:

Random somegenerator = new Random();

int a= somegenerator.Next(0,10);  //getal tussen 0 tot en met 9
int b= somegenerator.Next(5,101);  //getal tussen 5 tot en met 100
int c= somegenerator.Next(0,b);  //getal tussen 0 tot en met het getal dat de lijn ervoor werd gegenereerd.

Genereer kommagetallen met NextDouble

Met de NextDouble methode kan je kommagetallen genereren tussen 0.0 en 1.0 (1.0 zal niet gegenereerd worden).

Wil je een groter kommagetal dan zal je dit gegenereerde getal moeten vermenigvuldigen naar de range die je nodig hebt. Stel dat je een getal tussen 0.0 en 10.0 nodig hebt, dan schrijf je:

Random myran= new Random();
double randomgetal= myran.NextDouble() * 10.0;

Ik krijg dezelfde random getallen? Wat nu?

Kennisclip

Oefeningen

Oefening: H2-string-interpolation

Leerdoelen

  • gebruik van string interpolation

Functionele analyse

Oefening H1-maaltafels en H1-ruimte dienen we te herschrijven volgens de principes van string interpolation.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Zie oefening H1-maaltafels en H1-ruimte.

Technische hulp

Programmaverloop

Pas string interpolatie mbv $ (manier 2) toe.

Testscenario's

  • Zie oefening H1-maaltafels en H1-ruimte.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H2-systeem-informatie

Leerdoelen

  • gebruik van string interpolation

  • gebruik van Environment class

Functionele analyse

Maak een applicatie die de belangrijkste computer-informatie (geheugen, etc) aan de gebruiker toont.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Technische hulp

Programmaverloop

Pas string interpolatie mbv $ (manier 2) toe. De computerinformatie kan je verkrijgen mbv de Environment-klasse. Hier enkele voorbeelden (kijk zelf of er nog nuttige properties over je computer in staan en voorzie deze ook binnen jouw code):

WorkingSet geeft terug hoeveel geheugen het programma van windows toegewezen krijgt. Als je dus op 'run' klikt om je code te runnen dan zal dit programma geheugen krijgen en via WorkingSet kan het programma dus zelf zien hoeveel het krijgt. (wat een vreemde lange zin).

Zoals je ziet wordt het geheugen in bytes teruggegeven. Zorg ervoor dat het geheugen steeds in mega of gigabytes op het scherm wordt getoond.

Formatteer de informatie met behulp van de $-notatie zodat deze deftig getoond worden en de gebruiker snel de belangrijke informatie over z'n systeem te zien krijgt.

Testscenario's

  • wat gebeurt er wanneer je het datatype string zou wijzigen in int?

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H2-weerstandberekenaar-deel1

Leerdoelen

  • gebruik van math class

Functionele analyse

Stel dat je in het labo een weerstand vastneemt en je kent de kleurcodes van de streepjes wel, maar niet hoe je die kunt omzetten naar de effectieve weerstandswaarde. In dit programma kunnen we de gebruiker helpen.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Technische hulp

Programmaverloop

Maak een programma dat de weerstandwaarde berekent gebaseerd op:

  • Ring 1: die de tientallen voorstelt

  • Ring 2: die de eenheden voorstel

  • Ring 3: die de exponent (10 tot de macht) voorstelt. (tip:Math.Pow(10,ring3))

Gebruik drie variabelen van het type int. (we veronderstellen dus dat de gebruiker de kleurcode heeft omgezet naar een getal en dat toewijst aan de variabele)

Test dat je "formule/berekening" klopt om gebaseerd op 2 (of 3) ringen de weerstandswaarde te berekenen.

Testscenario's

  • wat gebeurt er wanneer je een hoger getal dan 9 zou invoeren?

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H2-weerstandberekenaar-deel2

Leerdoelen

  • gebruik van UNICODE

Functionele analyse

Zie deel 1.

Technische hulp

Programmaverloop

Zie deel 1 en plaats het geheel in een mooie UNICODE-tabel.

Hier enkele nuttige tekens:

of:

Testscenario's

  • wat gebeurt er wanneer je een waarde van circle 1, 2 of 3 uit meer dan twee cijfers bestaat?

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H2-shell-starter

Leerdoelen

  • gebruik van Process.Start()

  • verwerken van uitvoer

Functionele analyse

Je kan de output van een Process.Start() programma naar je console scherm sturen. Dit vereist wat meer code. Volgende voorbeeld zal de output van het commando ipconfig /all op het scherm tonen:

Onder macOS heb je een ander commando nodig. Gebruik daar "ifconfig" voor het (uitvoerbaar) bestand en geef een lege string mee voor de argumenten.

Technische hulp

Programmaverloop

Maak enkele kleine C# programma's die bepaalde shell-commando's zullen uitvoeren en die de uitvoer in hoofdletters weergeven in plaats van in de gewone vorm. Enkele nuttige shell-commando's in de netwerk-sfeer zijn bijvoorbeeld:

Testscenario's

  • Probeer van bovenstaande programma's al die, die compatibel zijn met je besturingssysteem.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Maak eenmalig een Random-generator aan: Random randomgen= new Random(); (wat dit juist wil zeggen zien we in ).

(Bron afbeelding: )

Gebruik $-string interpolatie om de informatie in de tabel te tonen zodat je volgende uitvoer kunt genereren:

Semester 2
Zie hier voor meer informatie indien je dezelfde random nummers genereert.
Random toepassen
Uw computer heeft een 64-bit besturingssysteem: True
De naam van uw pc is: LAPTOP
Uw pc heeft 4 processorkernen.
ikke is uw gebruikersnaam.
Je gebruikt 11 megabytes aan geheugen
bool is64bit = Environment.Is64BitOperatingSystem;
string pcName = Environment.MachineName;
int procCount = Environment.ProcessorCount;
string userName = Environment.UserName;
long memory = Environment.WorkingSet; //zal ongeveer 11 MB teruggeven.
Geef de waarde (uitgedrukt in een getal van 0 tot 9) van de eerste ring: 2
Geef de waarde (uitgedrukt in een getal van 0 tot 9) van de tweede ring: 2
Geef de waarde (uitgedrukt in een getal van -2 tot 7) van de derde ring (exponent): 2
Resultaat is 2200 Ohm, ofwel 22x100.
╔═══════════════╦═══════════════╗
║ 
╟───────────────╫───────────────╢
║ 
╚═══════════════╩═══════════════╝
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "ipconfig";
process.StartInfo.Arguments = "/all"; 
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start(); //start process

// Read the output (or the error)
string output = process.StandardOutput.ReadToEnd();
Console.WriteLine(output);
string err = process.StandardError.ReadToEnd();
Console.WriteLine(err);
//Continue
Console.WriteLine("Klaar");
hostname
arp -a
getmac (enkel onder Windows)
nslookup google.com

Beslissingen intro

Nu we de elementaire zaken van C# en Visual Studio kennen is het tijd om onze programma's wat interessanter te maken. De ontwikkelde programma's tot nog toe waren steevast lineair van opbouw, ze werden lijn per lijn uitgevoerd zonder de mogelijkheid om de flow van het programma aan te passen. Het programma doorliep de lijnen braaf na elkaar en wanneer deze aan het einde kwam sloot het programma zich af.

Onze programma's waren met andere woorden niet meer dan een eenvoudige oplijstingen van opdrachten. Je kan het vergelijken met een lijst die je vertelt over hoe je een brood moet kopen: 1. Neem geld uit spaarpot 2. Wandel naar de bakker om de hoek 3. Vraag om een brood 4. Krijg het brood 5. Betaal het geld aan de bakker 6. Keer huiswaarts 7. Smullen maar

Alhoewel dit algoritme redelijk duidelijk en goed zal werken, zal de realiteit echter zelden zo goed zijn. Een beter algoritme (dat foutgevoeliger is én interactieve voor de eindgebruiker) zal afhankelijk van de omstandigheden (bakker gesloten, geen geld meer, etc.) mogelijke andere stappen ondernemen. Het programma zal beslissingen maken.

Uitbreidingsoefeningen

Volgende oefeningen zijn al iets stevigers. Ze gebruiken concepten die nog niet aan bod gekomen zijn in de cursus en je wordt niet verondersteld ze te kunnen maken aan het begin van het semester. Je kan ze wel bekijken als je eerder al geprogrammeerd hebt of wanneer je aan het studeren bent voor het examen.

Oefening: H2-systeem-informatie-pro

Leerdoelen

  • gebruik van string interpolation

  • gebruik van System.IO

Functionele analyse

Je moet informatie over de harde schijven op je systeem weergeven.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Vrije ruimte op jouw c-schijf: 10803744768
Totale ruimte van jouw c-schijf: 159671906304

********************************************************************************
Geef met nummer 1 t/m 2 aan over welke harde schijf van jouw pc je info wenst: 1
De vrije ruimte van C:\ is 108 Gb

Technische hulp

Programmaverloop

Ook informatie over de harde schijven kan je verkrijgen (in bits). Dit vereist wel dat je bovenaan je programma volgende lijn bijschrijft: using System.IO.

Vervolgens kan je in je programma schrijven:

long cDriveInBytes = DriveInfo.GetDrives()[0].AvailableFreeSpace;  
long totalSize = DriveInfo.GetDrives()[0].TotalSize;

De lijn met using is om aan te geven dat we iets uit de System.IO bibliotheek nodig hebben, namelijk DriveInfo. Schrijven we dat niet dan moeten we in onze code DriveInfo aanroepen met z'n volledige path: System.IO.DriveInfo....

De 0 tussen rechte haakjes is de index van welke schijf je informatie wenst. 0 is de eerste harde schijf, 1 de tweede, enzovoort. (Ter info: dit zijn arrays, zie later)

Vraag aan de gebruiker het nummer van de harde schijf waar meer informatie over moet getoond worden.

Opgelet: sta toe dat de gebruiker 1 voor de eerste harde schijf mag gebruiken, 2 voor de tweede, enzovoort. Je zal dus in code nog manueel 1 moeten aftrekken van de invoer van de gebruiken. Bv:

int input= Convert.ToInt32(Console.ReadLine()) - 1 ;
long totalSize = DriveInfo.GetDrives()[input].TotalSize;

Testscenario's

  • wat gebeurt er wanneer je het datatype int zou wijzigen in string?

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H2-weerstandsberekendaar-deel3

Deze bouwt voort op H2-weerstandsberekendaar-deel2. Kan je afhankelijk van de ringwaarde het getal in de tabel in de juiste kleur zetten conform de weerstandskleuren? (tip: je zal Write en if moeten leren gebruiken)

https://www.esdsite.nl

Datatypes

Een essentieel onderdeel van C# is kennis van datatypes. Binnen C# zijn een aantal types gedefinieerd die je kan gebruiken om data in op te slaan. Wanneer je data wenst te bewaren in je applicatie dan zal je je moeten afvragen wat voor soort data het is. Gaat het om een getal, een geheel getal, een kommagetal, een stuk tekst of misschien een binaire reeks? Ieder datatype in C# kan één welbepaald soort data bewaren en dit zal telkens een bepaalde hoeveelheid computergeheugen vereisen.

Er zijn tal basistypes in C# gedeclareerd (zogenaamde primitieve datatypes). Dit semester leren we werken met datatypes voor:

  • Gehele getallen: sbyte, byte, short, ushort, int, uint, long

  • Kommagetallen: double, float, decimal

  • Tekst: char, string

  • Booleans: bool

Het datatype string heb je al gezien in het vorig hoofdstuk. Je hebt toen al een variabele aangemaakt van het type string door de zin string result;.

Verderop plaatsten we dan iets waar de gebruiker iets kan intypen in die variabele (toekenning in C# gaat van rechts naar links):

result = Console.ReadLine();

Basistypen voor getallen

Alhoewel een computer digitaal werkt en enkel 0'n en 1'n bewaard zou dat voor ons niet erg handig werken. C# heeft daarom een hoop datatypes gedefinieerd om te werken met getallen zoals wij ze kennen, gehele en kommagetallen. Intern zullen deze getallen nog steeds binair bewaard worden, maar dat is tijdens het programmeren zelden een probleem.

Onthoudt echter dat onderaan je programma steeds hardware zal draaien die binair werkt.

De basistypen van C# om getallen in op te slaan zijn:

  • Voor gehele getallen: sbyte, byte, short, ushort, int, uint, long

  • Voor kommagetallen: double, float, decimal

Ieder type hierboven heeft een bepaald bereik en hoeveelheid geheugen nodig. Je zal dus steeds moeten afwegen wat je wenst. Op een high-end pc met ettelijke gigabytes aan werkgeheugen (RAM) is geheugen zelden een probleem waar je rekening mee moet houden...Of toch: zoals met real-time shooters die miljoenen berekeningen (3D) per seconde moeten uitvoeren. Daar zal iedere byte tellen. Op andere apparaten (smartphone, arduino, smart fridges, etc.) is iedere byte geheugen nog kostbaarder. Kortom: kies steeds bewust het datatype dat het beste 'past' voor je probleem qua bereik, precisie en geheugengebruik.

Deze datatypes hebben allemaal een bepaald bereik, wat een rechtstreeks gevolg is van de hoeveelheid geheugen die ze innemen.

Gehele getallen

Voor de gehele getallen:

Type

Geheugen

Bereik

Meer info

sbyte

8 bits

-128 tot 127

byte

8 bits

0 tot 255

short

16 bits

-32768 tot 32767

ushort

16 bits

0 tot 65535

int

32 bits

-2 147 483 648 tot 2 147 483 647

uint

32 bits

0 tot 4294967295

long

64 bits

-9 223 372 036 854 775 808 tot 9 223 372 036 854 775 807

ulong

64 bits

0 tot 18 446 744 073 709 551 615

char

16 bits

0 tot 65535

We raden aan dat je de 'info' urls bekijkt om te ontdekken hoe je de literals van datatypes moet schrijven in C#.

Enkele opmerkingen bij deze tabel:

  • De s vooraan sbyte types staat voor signed: m.a.w. 1 bit wordt gebruikt om het + of - teken te bewaren.

  • De u vooraan ushort, uint en ulong staat voor unsigned. Het omgekeerde van signed dus. Kwestie van het ingewikkeld te maken. Deze twee datatypes hebben dus geen teken en zijn altijd positief.

  • char bewaard karakters. We zullen verderop dit datatype uitspitten en ontdekken dat karakters (alle tekens op het toetsenbord, inclusief getallen, leesteken, etc.) als gehele, binaire getallen worden bewaard. Daarom staat char in deze lijst.

  • Het grootste getal bij long is 2 tot de 63ste (

    negen triljoen tweehonderddrieëntwintig biljard driehonderd tweeënzeventig biljoen zesendertig miljard achthonderdvierenvijftig miljoen zevenhonderdvijfenzeventigduizend achthonderd en zeven). Dit zijn maar 63 bits?! Inderaad, de laatste bit wordt gebruikt om het teken te bewaren.

Kommagetallen

Voor de kommagetallen zijn er maar 3 mogelijkeden. Ieder datatype heeft een 'voordeel' tegenover de 2 andere, dit voordeel staat vet in de tabel:

Type

Geheugen

Bereik

Precisie

float

32 bits

±1.5 x 10-45 to ±3.4 x 1038

7 digits

double

64 bits

±5.0 x 10-324 to ±1.7 x 10308

15 digits

decimal

128 bits

±1.0 x 10-28 to ±7.9228 x 1028

28-29 digits

Zoals je ziet moet je bij kommagetallen een afweging maken tussen 3 even belangrijke criteria. Heb je ongelooflijk grote precisie nodig dan ga je voor een decimal. Wil je vooral erg grote of erg kleine getallen kies je voor double. De precisie van een getal is het aantal beduidende of significante cijfers. Het getal 12,45 heeft een precisie van 4. Zoals je merkt zal je dus zelden decimal nodig hebben, deze zal vooral nuttig zijn in wetenschappelijke programma's waar met erg exacte cijfers moet gewerkt worden.

Bij twijfel opteren we meestal voor kommagetallen om het double datatype te gebruiken. Bij gehele getallen kiezen we meestal voor int.

Boolean datatype

Het bool (boolean) is het eenvoudigste datatype van C#. Het kan maar 2 mogelijke waarden bevatten: true of false. 0 of 1 met andere woorden.

Het gebeurt vaak dat beginnende programmeurs een int variabele gebruiken terwijl ze toch weten dat de variabele maar 2 mogelijke waarden zal hebben. Om dus geen onnodig geheugen te verbruiken is het aan te raden om in die gevallen steeds met een bool variabele te werken.

Tekst/String datatype

We besteden verderop een heel apart hoofdstuk aan tonen hoe je tekst en enkele karakters kan bewaren in variabelen. Sneak preview:

  • Tekst kan bewaard worden in het string datatype

  • Een enkel karakter wordt bewaard in het char datatype dat we ook hierboven al even hebben zien passeren.

Casting, conversie en parsing

Wanneer je de waarde van een variabele wil toekennen aan een variabele van een ander type mag dit niet zomaar. Volgende code zal bijvoorbeeld een dikke error geven:

int age = 4.3;

Je kan geen appelen in peren veranderen zonder magie: in het geval van C# zal je moeten converteren of casten.

Dit kan op 3 manieren:

  • Via casting: de (klassieke) manier die ook werkt in veel andere programmeertalen.

  • Via de Convert bibliotheek van .NET. Deze staat omzettingen toe die handig zijn, maar niet op het niveau van de taal zijn vastgelegd.

  • Via parsing die we enkel terloops bespreken, maar die niet bij de leerstof van deze cursus hoort. Parsing betekent dat je tekst met een bepaalde vorm omzet naar data en werkt dus alleen op strings.

Casting

Het is uiteraard onmogelijk om een kommagetal aan een geheel getal toe te wijzen zonder dat er informatie verloren zal gaan. Toch willen we dit soms doen. Van zodra we een variabele van het ene type willen toekennen aan een variabele van een ander type en er dataverlies zal plaatsvinden dan moeten we aan casting doen.

Hierbij dien je aan de compiler te zeggen: "Volgende variabele die van het type x is, moet aan deze variabele van het type y toegekend worden. Ik besef dat hierbij data verloren kan gaan, maar zet de variabele toch maar om naar het nieuwe type, ik draag alle verantwoordelijkheid voor het verlies.".

Wat is casting

Casting heb je nodig om een variabele van een bepaald type voor een ander type te laten doorgaan. Stel dat je een complexe berekening hebt waar je werkt met verschillende types (bijvoorbeeld int, double en float). Door te casten voorkom je dat je vreemde resultaten krijgt. Je gaat namelijk bepaalde types even als andere types gebruiken.

Het is belangrijk in te zien dat het casten van een variabele naar een ander type enkel een gevolg heeft TIJDENS het uitwerken van de expressie waarbinnen je werkt. De variabele in het geheugen zal voor eeuwig en altijd het type zijn waarin het origineel gedeclareerd werd.

Je dient voornamelijk aan casting te doen wanneer je aan narrowing doet: een datatype omzetten naar een ander datatype dat een verlies aan data met zich zal meebrengen.

Casting duid je aan door voor de variabele of literal het datatype tussen haakjes te plaatsen naar wat het omgezet moet worden:

int mijngetal = (int)3.5;

of

double kommagetal = 13.8;
int kommaNietWelkom = (int)kommagetal;

Narrowing

Casting doe je dus wanneer je een variabele wilt toekennen aan een andere variabele van een ander type dat daar eigenlijk niet inpast. We moeten dan aan narrowing doen, letterlijk het versmallen van de data.

Bekijk eens het volgende voorbeeld:

double var1;
int var2;

var1 = 20.4;
var2 = var1;

Dit zal niet gaan. Je probeert namelijk een waarde van het type double in een variabele van het type int te steken. Dat gaat enkel als je informatie weggooit. Je moet aan narrowing doen.

Dit gaat enkel als je expliciet aan de compiler zegt: het is goed, je mag informatie weggooien, ik begrijp dat en zal er rekening mee houden. Dit proces van narrowing noemen we casting.

En je lost dit op door voor de variabele die tijdelijk dienst moet doen als een ander type, het nieuwe type, tussen ronde haakjes te typen, als volgt:

double var1;
int var2;

var1 = 20.4;
var2 = (int)var1;

Het resultaat in var2 zal 20 zijn (alles na de komma wordt bij casting van een double naar een int weggegooid).

Merk op dat var1 nooit van datatype is veranderd; enkel de inhoud ervan (20.4) werd eruit gehaald, omgezet ("gecast") naar 20 en dan aan var2 toegewezen dat enkel int aanvaardt.

Narowing in de praktijk

Stel dat temperatuurGisteren en temperatuurVandaag van het type int zijn, maar dat we nu de gemiddelde temperatuur willen weten. De formule voor gemiddelde temperatuur over 2 dagen is:

int temperatuurGemiddeld = (temperatuurGisteren + temperatuurVandaag)/2;

Test dit eens met de waarden 20 en 25. Wat zou je verwachten als resultaat? Inderdaad: 22,5 (omdat (20+25)/2 = 22.5) Nochtans krijg je 22 op scherm te zien en zal de variabele temperatuurGemiddeld ook effectief de waarde 22 bewaren en niet 22.5.

Het probleem is dat het gemiddelde van 2 getallen niet noodzakelijk een geheel getal is. Omdat de expressie enkel integers bevat (temperatuurGisteren, temperatuurVandaag en 2) zal ook het resultaat een integer zijn. In dit geval wordt alles na de komma gewoon weggegooid, vandaar de uitkomst.

Hoe krijgen we de correctere uitslag te zien? Door temperatuurGemiddeld als kommagetal te declareren (bijvoorbeeld door het type double):

double temperatuurGemiddeld = (temperatuurGisteren + temperatuurVandaag)/2;
double temperatuurGemiddeld = ((double)temperatuurGisteren + (double)temperatuurVandaag)/2;

Nu zal temperatuurGemiddeld wel de waarde 22.5 bevatten.

Widening

Casting is echter niet nodig als je aan widening doet (een kleiner type in een groter type steken), als volgt:

int var1;
double var2;

var1 = 20;
var2 = var1;

Deze code zal zonder problemen gaan. var2 zal de waarde 20.0 bevatten. De inhoud van var1 wordt verbreed naar een double, eenvoudigweg door er een kommagetal van te maken. Er gaat geen inhoud verloren echter. Je hoeft dus niet expliciet de casting-notatie zoals (double)var1 te doen, de computer ziet zelf dat hij de inhoud van var1 zonder dataverlies kan toekennen aan var2.

Conversie

Casting is een in de taal ingebakken manier van data omzetten, die vooral zeer nuttig is daar deze ook werkt in andere C#-related programmeertalen zoals C, C++ en Java.

Echter, .NET heeft ook ingebouwde conversie-methoden die je kunnen helpen om data van het ene type naar het andere te brengen. Het nadeel is dat ze iets meer typwerk (en dus meer code) verwachten dan bij casting. Al deze methoden zitten binnen de Convert-bibliotheek van .NET.

Het gebruik hiervan is zeer eenvoudig. Enkele voorbeelden:

int getal= Convert.ToInt32(3.2); //double to int
double anderGetal= Convert.ToDouble(5); //int to double
bool isWaar= Convert.ToBoolean(1); //int to bool
int userAge= Convert.ToInt32("19"); //string to int
int ageOther= Convert.ToInt32(anderGetal); //double to int

Je plaatst tussen de ronde haakjes de variabele of literal die je wenst te converteren naar een ander type. Merk op dat naar een int converteren met .ToInt32() moet gebeuren. Om naar een short te converteren is dit met behulp van .ToInt16().

Convert.ToBoolean verdient extra aandacht: Wanneer je een getal, eender welk, aan deze methode meegeeft zal deze altijd naar True geconverteerd worden. Enkel indien je 0, (int) of 0.0 ingeeft, dan krijg je False. In quasi alle andere gevallen krijg je altijd True.

Opgelet: de convert zal zelf zo goed mogelijk de data omzetten en dus indien nodig widening of narrowing toepassen. Zeker bij het omzetten van een string naar een ander type kijk je best steeds de documentatie na om te weten wat er intern juist zal gebeuren.

Ter info: Parsing

Voorlopig zullen we parsing niet nodig hebben. Voor de volledigheid plaatsen we deze informatie hier echter.

Naast conversie en casting bestaat er ook nog parsing.

Parsing is anders dan conversie en casting. Parsing zal je enkel nodig hebben dit jaar om tekst naar getallen om te zetten.

Ieder ingebouwd type heeft een .Parse() methode die je kan aanroepen om strings om te zetten naar het gewenste type. Parsing zal je echter minder vaak nodig hebben. Gebruik deze enkel wanneer

  1. Je een string hebt waarvan je weet dat deze altijd van een specifiek type zal zijn, bv een int, dan kan je Int32.Parse() gebruiken.

Voorbeeld van parsing:

int numVal = Int32.Parse("-105");
Console.WriteLine(numVal);

Oefeningen

Oefening: H1-variabelen_hoofdletters

Leerdoelen

  • gebruik van variabelen om input en output via Console.ReadLine en Console.WriteLine op te slaan en terug te tonen

Functionele analyse

Een applicatie vraagt je tekst in te voeren die dan daarna zal worden getoond met allemaal hoofdletters.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Technische hulp

Programmaverloop

Lees de gebruikersinvoer van de console en slaag dit op in een variabele.

Zet de inhoud van deze variabele om in hoofdletters. Je kan dit doen door ToUpper() toe te voegen aan een variabele van het type string. Bijvoorbeeld myText.ToUpper() in plaats van myText.

Uiteindelijk geef je dan deze variabele weer in de console.

Testscenario's

  • Voer tekst in met spaties

  • Voer tekst in van meer dan 100 karakters

  • Voer tekst in van 1 karakter

  • Voer geen tekst in

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H1-optellen

Leerdoelen

  • gebruik van variabelen om input via Console.ReadLine op te slaan

  • berekeningen met de opgeslagen data uitvoeren

  • het resultaat dat werd opgeslagen in een variabele via onsole.WriteLine te tonen

Functionele analyse

Een applicatie vraagt je twee getallen in te voeren. Na de invoer van het tweede getal worden beide getallen bij elkaar opgeteld. Het resultaat wordt uiteindelijk weergeven.

Technische analyse

UI

console applicatie

Werking

  1. De vraag wordt gesteld om een getal in te typen en daarna op enter/return te drukken.

  2. Er wordt gevraagd een tweede getal in te typen en dan op enter/return te drukken.

  3. De twee getallen worden opgeteld.

  4. Het resultaat wordt weergegeven.

voorbeeldinteractie(s)

Wat is het eerste getal?
> 1
Wat is het tweede getal?
> 4
De som is 5.

Technische hulp

Programmaverloop

Lees de gebruikersinvoer van de console en slaag dit op in een variabele voor wat het eerste getal betreft. Herhaal dit voor het tweede getal. Tel de twee getallen samen en bewaar deze in een derde variabele. Uiteindelijk geef je dan de inhoud van deze derde variabele weer in de console. Tip: getal1 = Convert.ToInt32(invoer1);

Testscenario's

  • Voer tekst in.

  • Voer een getal met 100 cijfers in.

  • Voer geen getal in.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H1-verbruik-wagen

Leerdoelen

  • gebruik van variabelen om input via Console.ReadLine op te slaan

  • berekeningen met de opgeslagen data uitvoeren

  • het resultaat dat werd opgeslagen in een variabele via onsole.WriteLine te tonen

Functionele analyse

Een applicatie zal voor jou het gemiddelde verbruik van een wagen berekenen.

Hiervoor worden volgende vragen gesteld:

  1. Hoeveel liter is er nog aanwezig in de benzinetank.

  2. Hoeveel liter zit er nog in de benzinetank na de rit.

  3. Ook de kilometerstand van bij de aanvang van de rit wordt gevraagd en ook deze nadat de rit werd uitgevoerd.

Op basis van deze parameters wordt het gemiddelde verbruik berekend en weergegeven.

Technische analyse

UI

console applicatie

Werking

  1. De vraag wordt gesteld om het aantal liter, aanwezig in de benzinetank, op te geven.

  2. Daarna wordt gevraagd om ook het aantal liter op te geven na de rit.

  3. De kilometerstand van de aanvang van de rit wordt gevraagd.

  4. Uiteindelijk ook de kilometerstand na het beëindigen van de rit wordt opgevraagd.

voorbeeldinteractie(s)

De screenshot vermeldt het afgeronde verbruik, maar dat hoef je niet te tonen. De instructie om af te ronden heb je nog niet gezien.

Technische hulp

Programmaverloop

Lees de gebruikersinvoer van de console en slaag dit op in variabelen.

Zorg ervoor dat je het juiste gegevenstype kiest voor de verschillende variabelen.

Nadien voer je de berekening uit om op basis van de ingevoerde gegevens het gemiddeld verbruik te berekenen (100 * (aantalLiterinTank1 - aantalLiterinTank2) / (kilometerstand2 - kilometerstand1))

Uiteindelijk geef je dan het resultaat weer in de console.

Testscenario's

  • Voer tekst in.

  • Voer een getal met 100 cijfers in.

  • Voer geen getal in.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H1-beetje-wiskunde

Leerdoelen

  • expressies schrijven

  • voorrang van operatoren

  • effect van operaties naargelang datatype begrijpen

Functionele analyse

Je schrijft een programma dat de rol vervult van een rekenmachine. Het voert volgende berekeningen uit:

  • -1 + 4 * 6

  • ( 35+ 5 ) % 7

  • 14 + -4 * 6 / 11

  • 2 + 15 / 6 * 1 - 7 % 2

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

23
5
12
3

Technische hulp

Programmaverloop

Eerst wordt een resultaat berekend, daarna wordt het geprint.

Testscenario's

  • Test uit met getallen van het type int.

  • Test uit met getallen van het type float.

Ondersteunend materiaal

Oefening: H1-gemiddelde

Leerdoelen

  • expressies schrijven

  • voorrang van operatoren

  • effect van operaties naargelang datatype begrijpen

Functionele analyse

Je schrijft een programma dat het gemiddelde van 18, 11 en 8 berekent, d.w.z. deze drie getallen optelt en de som deelt door drie.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

12

Technische hulp

Programmaverloop

Eerst wordt het resultaat berekend, daarna wordt het geprint.

Testscenario's

  • Test uit met getallen van het type int.

  • Test uit met getallen van het type float.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H1-maaltafels

Leerdoelen

  • de console leegmaken

  • werken met wiskundige operatoren

  • interactie met de gebruiker

Functionele analyse

Je schrijft een programma dat de tafel van vermenigvuldiging voor 411 geeft. Dit programma wacht steeds tot de gebruiker op ENTER duwt voor het het volgend resultaat toont. Verder maakt het steeds het scherm leeg voor het een nieuw resultaat toont. Zie "programmaverloop".

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

1 * 411 is 411.
2 * 411 is 822.

(enzovoort)

10 * 411 is 4110.

Technische hulp

Programmaverloop

Voor elk resultaat wordt het scherm eerst leeggemaakt. Daarna pas wordt het resultaat getoond. Wanneer de gebruiker op ENTER duwt, wordt deze handeling herhaald voor het volgende resultaat (of eindigt het programma, na het tiende resultaat). Het scherm leegmaken doe je met Console.Clear(). Plaats 411 ook in een variabele.

Testscenario's

  • Test uit zoals gegeven.

  • Test uit voor 511. Je zou maar één teken in je code moeten aanpassen als je de instructies hebt gevolgd.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Oefening: H1-ruimte

Leerdoelen

  • werken met kommagetallen

Functionele analyse

Je massa is overal dezelfde en wordt uitgedrukt in kilogram. Je gewicht daarentegen is afhankelijk van de zwaartekracht van de plek waar je bent en wordt uitgedrukt in Newton. Je hebt dus een ander gewicht op andere planeten. Zo is je gewicht veel groter op Jupiter dan op Mars, omdat Jupiter meer zwaartekracht uitoefent dan Mars. Schrijf een programma dat je gewicht op aarde omzet naar je gewicht op een ander hemellichaam. Je krijgt volgende omzettingstabel:

  • Mercurius: 0.38 (een persoon van 100kg voelt zich alsof hij 38kg weegt)

  • Venus: 0.91

  • Aarde: 1.00 (een persoon van 100kg voelt zich alsof hij 100kg weegt)

  • Mars: 0.38

  • Jupiter: 2.34

  • Saturnus: 1.06

  • Uranus: 0.92

  • Neptunus: 1.19

  • Pluto: 0.06

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

Op Mercurius heb je een schijnbaar gewicht van 26.22kg.
Op Venus heb je een schijnbaar gewicht van 62.79kg.
Op Aarde heb je een schijnbaar gewicht van 69kg.
Op Mars heb je een schijnbaar gewicht van 26.22kg.
Op Jupiter heb je een schijnbaar gewicht van 161.46kg.
Op Saturnus heb je een schijnbaar gewicht van 73.14kg.
Op Uranus heb je een schijnbaar gewicht van 63.48kg.
Op Neptunus heb je een schijnbaar gewicht van 82.11kg.
Op Pluto heb je een schijnbaar gewicht van 4.14kg.

Technische hulp

Programmaverloop

Plaats je gewicht in een variabele. Kies zelf een geschikt type.

Testscenario's

  • Test uit voor je eigen gewicht.

  • Test uit voor het gewicht van een persoon met een massa van 100kg.

Ondersteunend materiaal

Hou het voorlopig op de cursus.

Math-library en berekeningen

Berekeningen

Een groot deel van je leven als ontwikkelaar zal bestaan uit het bewerken van variabelen in code. Meestal zullen die bewerkingen voorafgaan van berekeningen. De Math bibliotheek zal ons hier bij kunnen helpen.

De Math bibliotheek

De Math bibliotheek bevat aardig wat handige methoden. Deze bibliotheek bevat methoden voor een groot aantal typische wiskundige methoden (sinus, cosinus, vierkantswortel, macht, afronden, etc.) en kan je dus helpen om leesbaardere expressies te schrijven.

Stel dat je de derde macht van een variabel getal wenst te berekenen. ZONDER de Math-bibliotheek zou dat er zou uitzien:

MET de bibliotheek kunnen we schrijven:

De Math bibliotheek ontdekken

Als je in Visual Studio Math schrijft, gevolgd door een punt . krijg je alles te zien wat de Math-bibliotheek kan doen:

Een doosje voor een naam wil zeggen dat het om een Methode gaat (zoals Console.ReadLine()). Een vierkantje met twee streepjes in zijn constanten (zoals Pi(π) en e).

Methoden gebruiken

De meeste methoden zijn zeer makkelijk in gebruik en werken op dezelfde manier. Meestal moet je 1 of meerdere "argumenten" tussen de haken meegeven en het resultaat moet je altijd in een nieuwe variabele opvangen. Enkele voorbeelden:

Twijfel je over de werking van een methode, gebruik dan de help als volgt:

  1. Plaats je cursor op Pow.

  2. Druk op F1 op je toetsenbord.

  3. Je krijgt nu de help-files te zien van deze methode op MDSDN.

PI (π)

Ook het getal Pi (3.141...) is beschikbaar in de Math-library. Het witte icoontje voor PI bij Intellisense toont aan dat het hier om een ‘field’ gaat; een eenvoudige variabele met een specifieke waarde. In dit geval gaat het zelfs om een const field, met de waarde van Pi van het type double.

Je kan deze als volgt gebruiken in berekeningen:

Lopende som

Een vaak voorkomende oefening is deze waarbij je een bepaalde variabele continue moet bijpassen. Stel bijvoorbeeld dat je aan de gebruiker de temperatuur van iedere dag vraagt om zo vervolgens het gemiddelde te berekenen. Dit kan je doen door middel van een lopende som: je gaat telkens het ingevoerde getal toevoegen aan wat je reeds hebt bewaard. Meestal dus met behulp van de += operator.

Wanneer we met loops leren werken zullen lopende sommen zéér nuttig worden.

Kennisclip

We zullen het bool datatype erg veel nodig hebben wanneer we met zullen werken, specifiek de die afhankelijk van de uitslag van een bool bepaalde code wel of niet zullen doen uitvoeren.

Meer info vind je later in .

Als we dit testen zal nog steeds de waarde 22 aan temperatuurGemiddeld toegewezen worden. De expressie rechts bevat enkel integers en de computer zal dus ook de berekening en het resultaat als integer beschouwen (). We moeten dus ook de rechterkant van de toekenning als double beschouwen. We doen dit, zoals eerder vermeld, door middel van casting, als volgt:

Je kan .

Je input van de gebruiker vraagt (bv via Console.ReadLine) en niet 100% zeker bent dat deze een getal zal bevatten, gebruik dan Int32.TryParse(). ()

Er zijn nog subtiele verschillen die we hier niet behandelen ().

vind je een tabel terug die uitlegt welke operaties voorrang hebben.

Schrijf de Methode zonder argumenten. Bijvoorbeeld Math.Pow() (je mag de rode error negeren).

beslissingen
if statements
dit hoofdstuk
lees hier hoe dit nu weer zat
alle conversie-mogelijkheden hier bekijken
info
zie
Hier
double result= getal*getal*getal;  //SLECHTE MANIER
double result= Math.Pow(getal, 3);
double sineHoekA= Math.Sin(345); //IN RADIALEN!
double DerdeMachtVan20= Math.Pow(20, 3);
double complex= 3+ DerdeMachtVan20 * Math.Round(sineHoekA);
public const double PI;
double straal= 5.5;
double omtrek= Math.PI * 2 * straal;
double totaal= 0.0;
Console.WriteLine("Geef temperatuur dag 1");
double inp= Convert.ToDouble(Console.ReadLine());
totaal+=inp;
Console.WriteLine("Geef temperatuur dag 2");
inp= Convert.ToDouble(Console.ReadLine());
totaal+=inp;

//enz
Console.WriteLine($"Gemiddelde temperatuur deze week was: {totaal/7}");
info
info
info
info
info
info
info
info
info
info
info
info
De Math-bibliotheek

Input verwerken en omzetten

Input van de gebruiker verwerken

Dit hoofdstuk is niet lang, maar het is wel een zeer belangrijk aspect van console-applicaties!

En applicatie die geen input van de gebruiker vergt kan even goed een screensaver zijn. We hebben reeds gezien hoe we met Console.ReadLine() de gebruiker tekst kunnen laten invoeren en die we dan vervolgens kunnen verwerken om bijvoorbeeld z'n naam op het scherm te tonen.

De uitdaging met ReadLine is dat deze ALTIJD een string teruggeeft:

string userInput= Console.ReadLine();

Willen we dat de gebruiker een getal invoert, bijvoorbeeld z'n leeftijd, dan zullen dit nog steeds als string moeten opvangen en vervolgens CONVERTEREN.

User input verwerken (dat een andere type dan string moet zijn) bestaat dus uit 3 stappen:

  • Input uitlezen met Console.ReadLine()

  • Input bewaren in een string variabele

  • De variabele converteren met Convert. bibliotheek naar het gewenste type

Input converteren

Om strings naar een ander type te converteren gebruiken we best de Convert.-bibliotheek (maar .Parse() kan ook). De volgende code zal je dus erg vaak moeten schrijven. Stel dat we aan de gebruiker z'n gewicht vragen, dan moeten we dus doen:

Console.WriteLine("Geef je gewicht");
string inputGewicht= Console.ReadLine();
double gewicht= Convert.ToDouble(inputGewicht);

Foutloze input

Voorgaande code veronderstelt dat de gebruiker géén fouten invoert. De conversie zal namelijk mislukken indien de gebruiker bijvoorbeeld IKWEEG10KG invoert in plaats van 10,3.

De komende hoofdstukken moet je er altijd van uitgaan dat de gebruiker foutloze input geeft.

Opgelet: de invoer van kommagetallen door de gebruiker is afhankelijk van de landinstellingen van je besturingssysteem. Staat deze in Belgisch/Nederlands dan moet je kommagetallen met een KOMMA(,) invoeren (dus 9,81), staat deze in het Engels dan moet je een PUNT(.) gebruiken (9.81).

Opgelet 2: In je C# code moet je doubles ALTIJD met een punt schrijven. Dit is onafhankelijk van je taalinstellingen.

Fouten in input

Debuggen

Debuggen

Debugging is een ESSENTIËLE SKILL. Zorg dat je vlot breakpoints kunt plaatsen om zo tijdens de uitvoer te pauzeren om de inhoud van je variabelen te bekijken (via het zogenaamde watch-venster). Gebruik vervolgens de "step"-buttons om door je code te 'stappen', lijn per lijn.

Kennisclip

Booleanse logica en operators

Relationele operators

Om beslissingen te kunnen nemen in C# hebben we een nieuw soort operators nodig. Operators waarmee we kunnen testen of iets waar of niet waar is. Dit doen we met de zogenaamde relationele operators. En guess what, je kent die al van uit het lager! Enkel de "gelijk aan" ziet er iets anders uit dan we gewoon zijn:

C#-syntax

Betekenis

>

groter dan

<

kleiner dan

==

gelijk aan

!=

niet gelijk aan

<=

kleiner dan of gelijk aan

>=

groter dan of gelijk aan

Logische operator

De logische EN, OF en NIET-operatoren die je kent van de booleaanse algebra kan je ook gebruiken in C#:

C#-syntax

Betekenis

&&

en-operator

||

of-operator

!

niet-operator

Je kan de niet-operator voor een expressie zetten om het resultaat hiervan om te draaien. Bijvoorbeeld:

!(0==2)  //zal true geven

Booleaanse expressies

Een booleaanse expressie is een stuk C# code dat een bool als resultaat zal geven.

De logische operators van hierboven zijn operators die een bool teruggeven. Ze zijn zogenaamde test-operators: ze testen of iets waar is of niet. "Is b kleiner dan c?" schrijf je dan als de booleaanse expressie: b<c

Test maar eens wat er op je scherm komt als je in code schrijft: Console.WriteLine(45<=55);.

Zoals verwacht zal er true op het scherm verschijnen.

Test jezelf

Wat zal de uitkomst zijn van volgende expressies? (protip: het zal steeds true of false zijn, niets anders)

  • 3>2

  • 4!=4

  • 4<5 && 4<3

  • "a"=="a" || 4>=3

  • (3==3 && 2<1) || 5!=4

  • !(4<=3)

  • true || false

  • !true && false

Voorgaande oefeningen komen uit een vorige kennistoets examen. Het is voornaam dat je dit soort expressies vlot kunt oplossen!

Kennisclip

If

In dit deel zullen we bekijken hoe we ons programma dynamischer kunnen maken met behulp van het if-statement.

If

De if is een van de elementairste constructies in een programmeertaal. De syntax is als volgt:

if (boolean expression) 
{
     // code hier moet uitgevoerd worden indien de booleaanse expressie waar is
}

Enkel indien de booleaanse expressie waar is, en dus true als resultaat heeft, zal de code binnen de accolades van het if-blok uitgevoerd worden. Indien de expressie niet waar is (false) dan wordt het blok overgeslagen en gaat het programma verder met de code eronder.

Een voorbeeld:

int number = 3;

if ( number < 5 )
        Console.WriteLine ("A");
Console.WriteLine("B");

De uitvoer van dit programma zal zijn:

A
B

Indien number groter of gelijk aan 5 was dan zou er enkel B op het scherm zijn verschenen. De lijn Console.WriteLine("B") zal sowieso uitgevoerd worden zoals je ook kan zien aan de volgende flowchart:

if met een block

Het is aangeraden om steeds na de if-expressie met accolades te werken. Dit zorgt ervoor dat alle code tussen het block (de accolades) zal uitgevoerd worden indien de booleanse expressie waar was. Gebruik je geen accolades dan zal enkel de eerste lijn na de if uitgevoerd worden bij true.

Een voorbeeld:

if ( number < 5 )
{
    Console.WriteLine ("C");
    Console.WriteLine ("D");
}

De booleaanse expressie die je tussen de if haakjes plaats moet een stuk code zijn dat altijd een bool als resultaat teruggeeft.

We verwachten dat je if altijd met een block gebruikt, omdat dat leesbaarder is. De versie zonder block zien we als een stijlfout!

Veelgemaakte if-fouten

Er zijn enkele veelgemaakte fouten waar je op moet letten:

Appelen en peren vergelijken

De types in je booleanse expressie moeten steeds vergelijkbaar zijn. Volgende code is dus fout: if( "4" > 3) daar we hier een string met een int vergelijken.

Accolades vergeten

Accolades vergeten plaatsen om een codeblock aan te duiden, maar je code toch zodanig outlinen (met tabs) dat het lijkt of je een heel codeblock hebt. Het gevolg zal zijn dat enkel de eerste lijn na de if zal uitgevoerd worden indien true. Gebruiken we de if met block van daarnet maar zonder accolades dan zal de laatste lijn altijd uitgevoerd worden ongeacht de if:

if ( number < 5 )
    Console.WriteLine ("C");
    Console.WriteLine ("D");

Merk ook op dat je code anders uitlijnen géén invloed heeft op de uitvoer (wat bijvoorbeeld wel zo is bij de programmeertaal Python).

Een puntkomma plaatsen na de booleanse expressie.

Dit zal ervoor zorgen dat er eigenlijk geen codeblock bij de if hoort en je dus een nietszeggende if hebt geschreven. De code na het puntkomma zal uitgevoerd worden ongeacht de if:

if ( number < 5 );
    Console.WriteLine ("C");
    Console.WriteLine ("D");

Gebruik relationele operatoren

Met de relationele operatoren (==, !=, <, >, <= en >=) kunnen we complexere expressies schrijven die als uitkomst waar (true) of niet waar (false) geven.

Een voorbeeld:

int a, b, c;  

a = 2;  
b = 3;  

if(a < b) 
{
    Console.WriteLine("a is less than b"); 
}

if(a == b) 
{
    Console.WriteLine("you won't see this");  
}

Console.WriteLine(); 

c = a - b; // c contains -1 

Console.WriteLine("c contains -1"); 
if(c >= 0) 
{
    Console.WriteLine("c is non-negative"); 
}
if(c < 0) 
{
    Console.WriteLine("c is negative"); 
}

Console.WriteLine(); 

c = b - a; // c now contains 1 
Console.WriteLine("c contains 1"); 
if(c >= 0) 
{
    Console.WriteLine("c is non-negative"); 
}
if(c < 0)
{
     Console.WriteLine("c is negative"); 
}

Uitvoer van bovenstaande code zal zijn:

a is less than b

c contains -1
c is negative

c contains 1
c is non-negative

We kunnen ook meerdere expressies combineren zodat we complexere uitdrukkingen kunnen maken. Hierbij kan je gebruik maken van de logische operatoren (&&, ||, !) .

Een voorbeeld:

int a = 5, b = 5, c = 10;

if (a == b)
{
        Console.WriteLine(a);
}

if ((a > c) || (a == b))
{  
    Console.WriteLine(b);
}

if ((a >= c) && (b <= c))
{
    Console.WriteLine(c);
}

Uitvoer van dit programma zal zijn:

5
5

If/else

Met if/else kunnen we niet enkel zeggen welke code moet uitgevoerd worden als de conditie waar is maar ook welke specifieke code indien de conditie niet waar (false) is. Volgend voorbeeld geeft een typisch gebruik van een if/else structuur om 2 waarden met elkaar te vergelijken:

int x = 10;

if ( x > 9 )
{
         Console.WriteLine ("x is greater than 9!");
}
else
{
         Console.WriteLine ("x is less than 9!");
}

Kan je zelf een flowchart van bovenstaande code tekenen? Try it!

If/else if

Met een if/else if constructie kunnen we meerdere criteria opgeven die waar/niet waar moeten zijn voor een bepaald stukje code kan uitgevoerd worden. Sowieso begint men steeds met een if. Als men vervolgens een else if plaats dan zal de code van deze else if uitgevoerd worden enkel en alleen als de eerste expressie (van de if) niet waar was en de expressie van deze else if wel waar is.

Een voorbeeld:

int x = 9;

if (x == 10)
{
     Console.WriteLine ("x is 10");
}
else if (x == 9)
{
     Console.WriteLine ("x is 9");
}
else if (x == 8)
{
     Console.WriteLine ("x is 8");
}

Merk op dat else if niet meer is dan een verkorte schrijfwijze voor nesting van een if in een else-block.

Nesting

We kunnen met behulp van nesting ook complexere programma flows maken. Hierbij gebruiken we de accolades om het blok code aan te duiden dat bij een if/else if/else hoort. Binnen dit blok kunnen nu echter opnieuw if/else if/else structuren worden aangemaakt.

Volgende voorbeeld toont dit aan (bekijk wat er gebeurt als je emergencyValve aan closed gelijkstelt):

int reactorTemp = 1500;
string emergencyValve = " ";

if (reactorTemp < 1000)
{
    Console.WriteLine("Reactor temperature normal");
}
else
{
    Console.WriteLine("Reactor temperature too high!");
    if (emergencyValve == "closed")
    {
        Console.WriteLine("Reactor meltdown in progress!");
    }
}

Oefeningen

Gebruik je kennis van debuggen om vanaf dit hoofstuk problemen op te lossen. Gebruik niet Console.WriteLine() om de waarde van een variabele te controleren at-runtime, maar gebruik daarentegen breakpoints!

Oefening: H3-geometric-fun

Leerdoelen

  • gebruik van de Math namespace

Functionele analyse

Je moet verschillende vaakgebruikte meetkundige berekeningen uitvoeren. Vraag aan de gebruiker een hoek in graden. Zet deze om naar radialen. Gebruik vervolgens de verschillende geometrische functies in de Math namespace om de sinus, cosinus en tangens van de hoek aan de gebruiker te tonen. Je moet eerst omzetten naar radialen omdat deze functies dat formaat verwachten. Toon alle resultaten tot twee cijfers na de komma.

Technische analyse

UI

console applicatie

voorbeeldinteractie(s)

H3-BMI-berekenaar

Leerdoelen

  • gebruik van de Math namespace

  • je programma's delen met anderen

  • kommagetallen parsen

  • stringinterpolatie en formattering

Functionele analyse

Maak een programma dat aan de gebruiker z'n lengte en gewicht vraagt en vervolgens z'n berekende BMI (Body Mass Index) toont.

Technische analyse

De formule voor de BMI is BMI = kg / m², waarbij kg het de massa van een persoon voorstelt en m zijn lengte in meter (dus typisch als kommagetal tussen 1 en 2). Je vindt een functie om getallen af te ronden in de Math namespace. Je kan hieraan als eerste waarde aan getal meegeven dat je wil afronden en als tweede een aantal cijfers na de komma.

UI

console applicatie

voorbeeldinteractie(s)

Oefening: H3-op-de-poef

Leerdoelen

  • tussenresultaten bijhouden

  • werken met de Math namespace

Functionele analyse

Een vaste klant in je café bestelt altijd "op de poef". Dat wil zeggen dat hij niet betaalt en dat z'n rekeningen worden neergeschreven. Ooit zal de klant dan gevraagd worden de hele som te betalen. Schrijf een programma dat 5 keer na elkaar aan de barman vraagt om een bedrag in te voeren. Het ingevoerde bedrag wordt opgeteld bij wat er reeds op de rekening staat. Na 5 keer wordt de totale som getoond alsook hoeveel weken het duurt indien de klant wekelijks 10 euro afbetaalt.

Technische analyse

Gebruik een variabele om de totale som van de rekeningen bij te houden. De bedragen zullen "vrij klein" zijn, dus nooit meer dan €100. Het zijn ook gehele getallen. Je hoeft geen interest aan te rekenen.

UI

console applicatie

voorbeeldinteractie(s)

Oefening: H3-feestkassa

Leerdoelen

  • tussenresultaten bijhouden

Functionele analyse

De plaatselijke voetbalclub organiseert een mosselfestijn. Naast mosselen met frietjes (20 EUR) bieden ze voor de kinderen de mogelijkheid om een koninginnehapje (10 EUR) te kiezen. Verder is er een ijsje als nagerecht voorzien (3 EUR). Om het gemakkelijk te maken kosten alle dranken 2 EUR.

Ontwerp een applicatie zodat de vrijwilliger aan de kassa alleen maar de juiste aantallen moet ingeven ,lijn per lijn. (frietjes, koninginenhapje, ijsje, drank) om de totaal te betalen prijs te berekenen.

Het resultaat wordt als volgt weergegeven: Het totaal te betalen bedrag is x EURO.

Technische analyse

Je gebruikt best een variabele om de voorlopig som bij te houden. Zo moet je minder code herhalen.

UI

console applicatie

voorbeeldinteractie(s)

H3-orakeltje

Leerdoelen

  • werken met willekeurige getallen

Functionele analyse

Het orakel zal enkel realistische getallen geven. Maw, getallen tussen de 5 en 125 jaar (onder de 5 zou grof zijn).

We gaan geregeld een oefening in een later hoofdstuk verder uitbreiden. Het orakeltje van Delphi is er zo eentje.

Technische analyse

Je moet hier een willekeurig getal bepalen en dat dan in een mooie vorm presenteren aan de gebruiker. Gebruik hiervoor de klasse Random.

UI

console applicatie

voorbeeldinteractie(s)

Oefening: H3-random-invoer

Pas oefening H3-op-de-poef aan zodat de gebruiker vervangen wordt door een randomgenerator en de gebruiker op de hoogte brengt van de gegenereerde getallen (tussen 1 en 50 inclusief). Dit levert een output als volgt:

En wat als je toch foute invoer wilt opvangen? Dan is TryParse je vriend. We zullen dit bespreken wanneer we aan Methoden komen. Ongeduldig? .

Neem volgende korte demonstratie van debugging door: .

Omzetting van graden naar radialen werkt als volgt, als rad een aantal radialen is en deg een aantal graden: rad=deg * (Π/180). De verschillende wiskundige functies die je nodig hebt, vind je (zie "Methods" in de linkerbalk). Het getal Π vind je (bij benadering) onder Math.PI. Zie het hoofdstuk rond werken met tekst om twee cijfers na de komma te tonen.

Bekijk de volgende en geef je uitvoerbaar bestand van het programma aan een klasgenoot. Kijk of hij het kan uitvoeren.

Maak een orakel/waarzegger, namelijk de kleine broer of zus van het . Het programma zal aan de gebruiker vertellen hoe lang deze nog zal leven. Bijvoorbeeld: "Je zal nog 15 jaar leven.".

Lees hier alles over TryParse
Debugging in Visual Studio
Debuggen in VS
Logische operators en expressies
Geef een hoek, uitgedrukt in graden.
> 45
45 graden is 0.79 radialen.
De sinus is 0.71.
De cosinus is 0.71.
De tangens is 1.00.
Hoe veel weeg je in kg?
> 69.0
Hoe groot ben je in m?
> 1.78
Je BMI bedraagt 21.78.
Voer bedrag in?
> 12
De poef staat op 12 euro
Voer bedrag in?
> 14
De poef staat op 26 euro
Voer bedrag in?
> 3
De poef staat op 29 euro
Voer bedrag in?
> 8
De poef staat op 37 euro
Voer bedrag in?
> 2
De poef staat op 39 euro
*************************
Het totaal van  de poef is 39 euro.
Dit zal 4 afbetalingen vragen.
Mosselen met frietjes?
> 3
Koninginnenhapjes?
> 5
Ijsjes?
> 2
Dranken?
> 5
Het totaal te betalen bedrag is 126 EURO
Je zal nog 7 jaar leven.
Je zal nog 64 jaar leven.
Voer bedrag in?
Automatisch gegenereerd: 12
De poef staat op 12 euro
Voer bedrag in?
Automatisch gegenereerd: 14
De poef staat op 26 euro
Voer bedrag in?
Automatisch gegenereerd: 3
De poef staat op 29 euro
Voer bedrag in?
Automatisch gegenereerd: 8
De poef staat op 37 euro
Voer bedrag in?
Automatisch gegenereerd: 2
De poef staat op 39 euro
*************************
Het totaal van  de poef is 39 euro.
Dit zal 4 afbetalingen vragen.
hier
kennisclip
Orakel van Delphi

Scope van variabelen

Scope van variabelen

De locatie waar je een variabele aanmaakt bepaald de scope, oftewel de zichtbaarheid, van de variabele. Eenvoudig gezegd zullen steeds de omliggende accolades de scope van de variabele bevatten. Indien je de variabele dus buiten die accolades nodig hebt dan heb je een probleem: de variabele is enkel bereikbaar binnen de accolades vanaf het punt in de code waarin het werd aangemaakt.

Zeker wanneer je begint met if, loops, methoden, etc. zal de scope belangrijk zijn: deze code-constructies gebruiken steeds accolades om codeblocks aan te tonen. Een variabele die je dus binnen een if-blok aanmaakt zal enkel binnen dit blok bestaan, niet erbuiten.

if( something == true)
{
    int getal = 0 ;  //Start scope getal
    getal = 6;
} // einde scope getal

Console.WriteLine(getal); // zal niet werken daar de scope van getal al gedaan was

Wil je dus getal ook nog buiten de if gebruiken zal je je code moeten herschrijven zodat getal VOOR de if wordt aangemaakt:

int getal = 0 ;  //Start scope getal
if( something == true)
{
    getal = 6;
} 

Console.WriteLine(getal);

De scope van variabelen is soms wat verwarrend maar wel een onderdeel dat je deze hele cursus zal zien terugkomen.

Variabelen met zelfde naam

Zolang je in de scope van een variabele bent kan je geen nieuwe variabele met dezelfde naam aanmaken:

Volgende code is dus niet toegestaan:

{
    int getal = 0;
    {
        int getal = 5; //Deze lijn is niet toegestaan
    }

}

Je krijgt de error: A local variable named 'getal' cannot be declared in this scope because it would give a different meaning to 'getal', which is already used in a 'parent or current' scope to denote something else

Enkel de tweede variabele een andere naam geven is toegestaan in het voorgaande geval.

Dit is wel geldig, daar de scope van de eerste variabele afgesloten wordt door de accolades:

{
    int getal = 0 ;
    //....
}
//Verder in code
{
    int getal = 5;
}

Kennisclip

Loops intro

Herhalingen (loops) creëer je wanneer bepaalde code een aantal keer moet herhaald worden. Hoe vaak de herhaling moet duren is afhankelijk van de conditie die je hebt bepaald.

In het vorige hoofdstuk leerden we hoe we met behulp van beslissingen onze code konden branchen, aftakken zodat andere code werd uitgevoerd afhankelijk van de staat van bepaalde variabelen of invoer van de gebruiker. Wat we nog niet konden was terug naar boven vertakken. Soms willen we dat een heel stuk code 2 of meerdere keren moet uitgevoerd worden tot aan een bepaalde conditie wordt voldaan. "Voer volgende code uit tot dat de gebruiker 666 invoert."

Door herhalende code met loops te schrijven maken we onze code korter en bijgevolg ook minder foutgevoelig en beter onderhoudbaar.

Van zodra je dezelfde lijn(en) code onder elkaar in je code ziet staan (door bijvoorbeeld te copy pasten) is de kans zéér groot dat je dit korter kunt schrijven met loops.

Soorten loops

Er zijn verschillende soorten loops:

  • Definite of counted loop: een loop waar het aantal iteraties vooraf van gekend is. (bv. alle getallen van 0 tot en met 100 tonen)

  • Indefinite of sentinel loop: een loop waarvan op voorhand niet kan gezegd worden hoe vaak deze zal uitgevoerd worden. Input van de gebruiker of een interne test zal bepalen wanneer de loop stopt (bv. "Voer getallen in, voer -1 in om te stoppen" of "Bereken de grootste gemene deler")

  • Oneindige loop: een loop die nooit stopt. Soms gewenst (bv. de game loop) of, vaker, een bug.

Loops in C#

Er zijn 3 manieren om zogenaamde loops te maken in C#:

  • while: zal 0 of meerdere keren uitgevoerd worden

  • do while: zal minimaal 1 keer uitgevoerd worden

  • for: een alternatieve iets compactere manier om loops te beschrijven

Voorts zullen we ook een speciale loop variant zien in het volgende semester wanneer we arrays en objecten leren kennen:

NEVER EVAH USE GOTO

Het moet hier alvast even uit m'n systeem. goto is weliswaar een officieel C# keyword, toch zal je het in deze cursus nooit zien terugkomen in code. Je kan alle problemen in je algoritmes oplossen zonder ooit goto nodig te hebben.

Voel je toch de drang: don't! Simpelweg, don't. Het is het niet waard. Geloof me.

NEVER USE GOTO.

Programma flow analyse

Programma flow analyse

Door een flowchart op te stellen is het vaak veel eenvoudiger om een programma ofwel te analyseren (van code naar idee) ofwel om een programma te schrijven (van idee naar code).

Een flowchart (letterlijk: stroomkaart) of stroomdiagram is een schematische beschrijving van een proces. Met een flowchart kan je vaak ingewikkelde stukken code visualiseren waardoor het geheel plots niet meer zo ingewikkeld is.

Een flowchart bestaat uit een aantal elementen:

  • Pijl: een pijl geeft aan naar welk volgende blok wordt gegaan. Indien boven de pijl een bepaalde waarde staat wil dit zeggen dat deze pijl enkel wordt gevolgd als de uitkomst van het vorige blok de getoonde waarde geeft.

  • Start en einde: aangegeven met een cirkel met daarin de woorden "Start" of "Einde"

  • Verwerk-stap: een statement zoals "Voeg 1 toe aan X" wordt in een rechthoek geplaatst. Alle code die geen invoer nodig heeft zet je in een rechthoek.

  • Input/output: Een parallellogram gebruik je om in-of uitvoer van de gebruiker of scherm te tonen. Bv "Verkrijg X van gebruiker" of "Toon volgende zin op het scherm".

  • Condities en beslissingen: Een ruit wordt gebruikt wanneer een beslissing moet genomen worden. De condities van if en while-loops zet je dus in een ruit. De pijlen die eruit volgen geven aan welke pijl moet gevolgd worden gegeven een bepaalde waarde van de conditie.

Flow-elementen

We tonen nu kort de verschillende program flow elementen en hoe ze in een flowchart voorkomen.

If-element

If-else element

While-element

Do while-element

Merk op dat bij if en if-else de flow niet naar een eerder punt in de code gaat. Dit is dus de manier om een while/do while te herkennen: er wordt naar een eerder punt in de code gegaan, een punt waar we reeds geweest waren

For-element

Voorbeeld flowchart

Door de eerder beschreven elementen nu samen te voegen kunnen we een programma als een flowchart voorstellen. Stel dat we een programma "Faculteit" maken. Hierin is het de bedoeling om de faculteit van een gegeven getal N dat door de gebruiker wordt ingevoerd, te berekenen (bijvoorbeeld N=5 geeft dan 5! = 5x4x3x2x1 = 120 ). De finale flowchart ziet er als volgt uit:

Zoals verteld kunnen we een flowchart in beide richtingen gebruiken. We hebben de flowchart hiervoor gemaakt, gebaseerd op onze oplossing van het vorige labo. Maar stel dat je deze flowchart krijgt, dan kan je dus ook deze chart rechtstreeks omzetten in C#.

Code 2 flow

enum

Enum datatypes

Delen van dit hoofdstuk komen uit Visual C# 2012 - De basis (Sander Gerz)

Dit is nog zo'n onderschat hoofdstuk. Enums zijn wat raar in het begin, maar van zodra je er mee weg bent zal je niet meer zonder kunnen!

De bestaansreden voor enums

Stel dat je een programma moet schrijven dat afhankelijk van de dag van de week iets anders moet doen. In een wereld zonder enums (enumeraties) 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

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. ?

  • De code is niet erg leesbaar. dagKeuze==2? Was dat nu dinsdag of woensdag (want misschien was maandag 0 i.p.v. 1).

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" bewaard dan zal de if al niet werken. Iedere schrijffout of variant zal falen.

Enumeraties: het beste van beide wereld.

Enumeraties (enum) zijn een C# syntax die bovenstaand probleem oplost en het beste van beide samenvoegt: leesbaardere code en visual studio kan je helpen met minder foutgevoelige foute schrijven.

Het keyword enum geeft aan dat we een nieuw type maken dat maar enkele mogelijke waarden kan hebben. Nadat we dit nieuwe type hebben gedefiniëerd kunnen we variabele van dit nieuwe type aanmaken. De variabele die we dan later maken zal van dit type zijn en enkel de opgegeven waarden mogen bevatten. Ook zal IntelliSense van Visual Studio je de mogelijke waarden helpen invullen.

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;

Zelf enum maken

Zelf een enum type maken 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. In je console-applicaties moet dit binnen class Program gebeuren, maar niét binnen de (main) methoden:

Vanaf nu kan je variabelen van het type Weekdagen aanmaken.

Merk op dat er geen puntkomma achteraan komt.

Locatie enum definitie

Let er op dat je je enum op de juiste locatie in je code schrijft:

Dit is fout:

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 gedefiniëerd werden. De code is nu ook een pak leesbaarder geworden.

Enums en beslissingen werken graag samen

Ook de beslissingsstructuren worden leesbaarder:

of een switch:

Conversie

Intern worden de enum-variabelen als ints bewaard. In het geval van de Weekdagen zal maandag de waarde 0 krijgen, dinsdag 1, etc.

Wil je dus bijvoorbeeld 1 dag bijtellen dan kan je schrijven:

Andere interne waarde

Standaard worden de waarden dus genummerd intern beginnende bij 0, enz. Je kan dit ook manueel veranderen door bij het maken van de enum expliciet aan te geven wat de interne waarde moet zijn, als volgt:

De dagen zullen nu vanaf 1 genummerd worden, dus WeekDagen.Woensdag zal de waarde 3 hebben.

We kunnen ook nog meer informatie meegeven, bijvoorbeeld:

In dit geval zullen Maandag tot Vrijdag intern als 1 tot en met 5 bewaard worden, Zaterdag als 50, en Zondag als 60.

Why should I care?

Je kan perfect leven zonder enum. Vele programmeurs voor je hebben dit bewezen. Echter, van zodra ze enumontdekten (en begrepen) zijn nog maar weinig programmeurs ervan af gestapt.

De eerste kennismaking met enumeraties is wat bevreemdend: je kan plots je eigen datatypes aanmaken?! Van zodra je ze in de vingers hebt zal je ontdekken dat je veel leesbaardere code kunt schrijven én dat Visual Studio je kan helpen met het opsporen van bugs.

Wanneer gebruik je enum? Telkens je een variabele (of meerdere) nodig hebt waarvan je perfect op voorhand weet welke handvol mogelijke waarde ze mogen hebben. Ze worden bijvoorbeeld vaak gebruikt in finite state machines. Bij game development willen we bijhouden in welke staat het programma zich bevindt: intro, startmenu, ingame, gameover, optionsscreen, etc. Dit is een typisch enum verhaal. We definiëren hiervoor het volgende type:

En vervolgens kunnen we dan met een eenvoudige switch in ons hoofdprogramma snel de relevante code uitvoeren:

Kennisclip

Oefeningen deel 1

Vanaf nu zul je véél meer oefeningen voorgeschoteld krijgen dan je kan afwerken in 1 labo tijd (I dare you ;). Selecteer zelf de oefeningen die je wenst te doen en sla die over waarvan je overtuigd bent ze al te kunnen. De oefening zijn , in de mate van het mogelijke, gerangschikt op moeilijkheid.

De opwarmers

De oefeningen

Indien niet expliciet vermeld mag je kiezen met wat voor loop (for, while, do while) je het probleem zal oplossen.

Tafels van vermenigvuldigen

Gebruik de kracht van loops om pijlsnel alle tafels van 1 tot en met 10 van vermenigvuldigen op het scherm te tonen (dus van 1x1 tot 10x10 en alles daartussen)

RNA Transscriptie

DNA heeft steeds een RNA-complement (DNA is het gevolg van RNA transscriptie). Schrijf een programma dat een ingevoerde DNA-string omzet naar een RNA-string. De gebruiker voert steeds 1 DNA-nucleotide in per keer en duwt op enter, de RNA string wordt steeds groter. De omzetting is als volgt:

  • G wordt C

  • C wordt G

  • T wordt A

  • A wordt U

Als de gebruiker dus ACGTGGTCTTAA heeft ingevoerd moet het resultaat: UGCACCAGAAUU zijn.

Ga er van uit dat de gebruiker letter per letter invoert (telkens dus enter na een letter) en je de omgezette string doet groeien (m.b.v +=).

PRO Armstrong nummer

Een getal is een narcistisch getal of armstronggetal als het de som is van zijn eigen cijfers elk tot de macht verheven van het aantal cijfers.

  • 9 is een Armstrong nummer, want 9 = 9^1 = 9

  • 10 is geen Armstrong nummer, want 10 != 1^2 + 0^2 = 1

  • 153 is een Armstrong nummer, want: 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153

  • 154 is geen Armstrong nummer, want: 154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190

    Schrijf een programma dat aan de gebruiker een getal vraagt en vervolgens toont of het ingevoerde getal een Armstrong-nummer is of niet.

Je zou deze oefening kunnen oplossen door het ingevoerde getal als string op te splitsen in individuele char's. Maar ik raad je aan om de "wiskunde" weg te volgen zodat je terdege leert met loops te wiskunde.*

Tip 1: Stel dat je het getal 4560 hebt:

  • Eerst deel je 4563 door 1000. Dit geeft 4.

  • We trekken 4x1000 van 4563 af. Dit geeft 563.

  • Deel 563 door 100. Dit geeft 5.

  • We trekken 5x100 van 563 af. Dit geeft 63.

  • Deel 63 door 10. Dit geeft 6.

  • We trekken 6 x 10 van 63 af. Dit geeft 3

Tip 2: Je kan aan een string vragen hoe groot deze is als volgt:

Je kan dan nu met Math.Pow(10,lengte-1) berekenen vanaf welke exponent van 10 we moeten beginnen werken.

Euler project

Indien we alle natuurlijke getallen van 0 tot en met 10 oplijsten die een meervoud van 3 of 5 zijn, dan krijgen we de getallen 3,5,6,9 en 10. De som van deze 4 getallen is 33. Maak nu een programma dat de som van alle veelvouden van 3 of 5 weergeeft onder van 0 tot 1000 (dit zou 234168) moeten geven.

Tip: module is je grote held hier. Een getal is een veelvoud van x indien getal%x 0 als resultaat geeft.

PRO: For doordenker

Schrijf een programma dat de volgende output geeft , gegeven dat de gebruiker een maximum waarde invoert , dus als hij 4 ingeeft dan zal de driehoek maximum 4 breed worden. Gebruik enkel forloops!

PRO: For doordenker extra

Schrijf een programma dat de volgende output geeft (zie ook WhileDoordenker van vorige labo), gegeven dat de gebruiker een maximum waarde invoert die opgeeft uit hoeveel lijnen de boom bestaat. Maak enkel gebruik van for-loops.

Oefeningen deel 3

Oefening: H5-boekhouder

Leerdoelen

  • een taak herhaaldelijk uitvoeren met een lus

Functionele analyse

Maak een 'boekhoud-programma': de gebruiker kan continu positieve en negatieve getallen invoeren. Dit programma houdt volgende zaken bij:

  • de totale balans

  • de som van de positieve getallen

  • de som van de negatieve getallen

  • het gemiddelde

Technische analyse

Voor de eerste drie zaken kom je toe met een variabele. Voor de laatste is dit lastiger, omdat elk nieuw getal een kleiner effect heeft om het gemiddelde dan het vorige. Je houdt beter een teller bij met het aantal ingevoerde getallen. Dan is het gemiddelde de totale balans gedeeld door het aantal ingevoerde getallen.

UI

console applicatie

voorbeeldinteractie(s)

(Dit programma kan blijven verder lopen zo lang je wil.)

Oefening: H5-schaar-steen-papier

Leerdoelen

  • een taak herhaaldelijk uitvoeren met een lus

Functionele analyse

Maak een applicatie waarbij de gebruiker steen-schaar-papier met de computer kan spelen. De gebruiker kiest telkens steen, schaar of papier en drukt op enter. Vervolgens kiest de computer willekeurig steen, schaar of papier.

Vervolgens krijgt de winnaar 1 punt:

  • Steen wint van schaar, verliest van papier

  • Papier wint van steen, verliest van schaar

  • Schaar wint van papier, verliest van steen

  • Indien beide hetzelfde hebben wint niemand een punt.

De eerste (pc of gebruiker) die 10 punten haalt wint.

Technische analyse

  • Genereer een willekeurig getal tussen 1 en 3 om de computer te laten kiezen.

  • Teken een flowchart!

UI

console applicatie

voorbeeldinteractie(s)

(Helemaal op het einde)

of

Hopelijk kan volgende kennisclip je helpen: .

.

Via de kan je heel eenvoudig een flowchart genereren van pseudocode.

Volgende conversies met behulp van zijn dan ook perfect toegelaten:

Bekijk maak de oefeningen 8 tot en met 13 van hoofdstuk 4 in volgende

Ter info: Dit document staat ook in de lijst onderaan bij de nuttige extra's .

Maak volgende opdracht van :

Kennisclip "Scope van variabelen
Scope
foreach
Lees gerust hier waarom
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..
enum Weekdagen {Maandag, Dinsdag, Woensdag, Donderdag, Vrijdag, Zaterdag, Zondag}
public class Program
{
    enum Weekdagen {Maandag, Dinsdag, Woensdag, Donderdag, Vrijdag, Zaterdag, Zondag}

    public static void Main(string[] args)
    {

    }
}
public class Program
{
    public static void Main(string[] args)
    {
        enum Weekdagen {Maandag, Dinsdag, Woensdag, Donderdag, Vrijdag, Zaterdag, Zondag}   
    }
}
Weekdagen dagKeuze;
Weekdagen andereKeuze;
dagKeuze = Weekdagen.Donderdag;
if(dagKeuze == Weekdagen.Woensdag)
switch(dagKeuze)
{
    case Weekdagen.Maandag:
        Console.WriteLine("It's monday!");
        break;
    case Weekdagen.Dinsdag:
     //etc.
}
int keuze = 3;

Weekdagen dagKeuze = (Weekdagen)keuze;

//dagKeuze zal de waarde Weekdagen.Donderdag hebben
Weekdagen dagKeuze= Weekdagen.Dinsdag;
int extradag= (int)dagKeuze + 1;
Weekdagen nieuweDag= (Weekdagen)extradag;
//extraDag heeft de waarde Weekdagen.Woensdag
enum WeekDagen {Maandag=1, Dinsdag, Woensdag, Donderdag, Vrijdag, Zaterdag, Zondag}
enum WeekDagen {Maandag=1, Dinsdag, Woensdag, Donderdag, Vrijdag, Zaterdag=50, Zondag=60}
enum gamestate {intro, startmenu, ingame, gameover, optionsscreen}
//Bij opstart:
gamestate playerGameState= gamestate.intro;

//later:
switch(playerGameState)
{
    case gamestate.intro:
        //show fancy movie
        break;
    case gamestate.startmenu:
        //show start menu
        break;
    //etc...
int lengte= myInputGetal.Length;  //veronderstellend dat myInputGetal van het type string is
*
**
***
****
***
**
*
     *
    ***
   *****
  *******
 *********
***********
Geef een getal
> 7
De balans is 7
De som van de positieve getallen is 7
De som van de negatieve getallen is 0
Het gemiddelde is 7
Geef een getal
> -3
De balans is 4
De som van de positieve getallen is 7
De som van de negatieve getallen is -3
Het gemiddelde is 2
Geef een getal
> 11
De balans is 15
De som van de positieve getallen is 18
De som van de negatieve getallen is -3
Het gemiddelde is 5
Maak een keuze:
1 voor schaar
2 voor steen
3 voor papier
> 2
De computer kiest steen!
Niemand wint deze ronde!
Jij hebt 0 punten, de computer heeft 0 punten.
Maak een keuze:
1 voor schaar
2 voor steen
3 voor papier
> 1
De computer kiest papier!
Jij wint deze ronde!
Jij hebt 1 punt, de computer heeft 0 punten.
Maak een keuze:
1 voor schaar
2 voor steen
3 voor papier
> 1
De computer kiest steen!
De computer wint deze ronde!
Jij hebt 1 punt, de computer heeft 1 punt.
Jij hebt 10 punten, de computer heeft 8 punten.
Jij bent gewonnen!
Jij hebt 8 punten, de computer heeft 10 punten.
De computer is gewonnen!

Oefeningen

Sommige oefeningen zijn van de vorm "Maak een methode die...". Het is steeds de bedoeling dat je de werking van je methode ook test in je Main door deze aan te roepen.

Oefening: H6-veel-kleintjes

Leerdoelen

  • methodes

Functionele analyse

Schrijf een hele reeks methodes die je samen test:

  1. Methode Square die het kwadraat van een ingevoerd getal berekent.

  2. Methode Radius die de straal van een cirkel kan berekenen waarvan je de diameter meegeeft.

  3. Methodes Circumference en Surface (in de formule gebruik je Math.PI).

  4. Methode Largest die het grootste van 2 getallen teruggeeft.

  5. Methode IsEven die bepaalt of een getal even of oneven is (geeft een bool terug die true is indien even).

  6. Methode ShowOdd die alle oneven getallen van 1 tot n toont waarbij n als parameter wordt meegegeven.

Technische analyse

Schrijf een voorbeeldprogramma dat in de Main-methode elke methode kort demonstreert. Toon alle getallen tot op twee cijfers na de komma. Voor ShowOddNumbers kan je nog geen resultaat teruggeven, maar je kan het wel afprinten.

UI

console applicatie

voorbeeldinteractie(s)

Wat moet ik kwadrateren?
> 2
4
Wat is de diameter?
> 6
3
Wat is de diameter?
> 2
De omtrek is 6.28
De oppervlakte is 3.14
Welke twee getallen wil je vergelijken?
> 3
> 9
9 is het grootste getal
Geef een getal en ik zeg of het even is:
> 4
Het getal is even.
Geef een getal en ik zoek de oneven getallen:
> 7
1
3
5
7

Oefening: H6-voorstellen

Leerdoelen

  • methodes

Functionele analyse

Maak een methode die jezelf voorstelt op het scherm in de vorm van "Ik ben Tim Dams, ik ben 18 jaar oud en woon in de Lambrisseringsstraat 666".

Technische analyse

Je persoonlijke informatie mag hardcoded in je methode staan. Bedoeling is dat je de methode kan aanroepen als volgt:

MyIntro();

Deze methode voert een taak uit, maar geeft geen antwoord dat je verder zou kunnen gebruiken. Het return type is dan ook void.

UI

console applicatie

voorbeeldinteractie(s)

Ik ben Tim Dams, ik ben 18 jaar oud en woon in de Lambrisseringsstraat 666

Oefening: H6-voorstellen-plus

Leerdoelen

  • methodes-met-parameters

Functionele analyse

Maak een flexibelere versie van H6-voorstellen, die je persoonlijke gegevens als argumenten meekrijgt.

Technische analyse

Je persoonlijke informatie wordt meegegeven via drie parameters: één voor de naam, één voor de leeftijd, één voor de straat. Je moet de methode dus zo kunnen oproepen:

MyIntro("Tim Dams", 18, "Lambrisseringsstraat 666");

UI

console applicatie

voorbeeldinteractie(s)

Ik ben Tim Dams, ik ben 18 jaar oud en woon in de Lambrisseringsstraat 666

Oefening: H6-grootste-methode

Leerdoelen

  • methodes

Functionele analyse

Schrijf een methode die drie ints aanvaardt en de grootste als resultaat teruggeeft.

Technische analyse

Aangezien het maar om 3 argumenten gaat, kan je dit oplossen met ifs die het eerste en het tweede argument vergelijken, het eerste en het derde argument,... Test je methode uit in een voorbeeldprogrammaatje.

UI

console applicatie

voorbeeldinteractie(s)

Geef 3 ints.
> 7
> 10
> 4
Het grootste getal is 10.

Oefening: H6-paswoord-generator

Leerdoelen

  • methodes

Functionele analyse

Maak een paswoord generator die paswoorden van bepaalde lengte genereert en bestaat uit willekeurige letters, hoofdletters en cijfers. Plaats deze code in een methode met 1 parameter, namelijk de lengte van het paswoord dat gemaakt moet worden. De methode geeft het gegenereerde paswoord terug als resultaat.

Technische analyse

Maak gebruik van Random en een for-lus. Een Random genereert normaal alleen getallen, maar via casting kan je die getallen omzetten in chars. Raadpleeg een Unicode tabel voor de juiste getallen of (iets sneller) cast eerst 'a' en 'z' naar getallen en gebruik die om te bepalen welke willekeurige resultaten je mag genereren. Demonstreer je methode met een kort programma.

UI

console applicatie

voorbeeldinteractie(s)

Hoe veel karakters moet je wachtwoord bevatten?
> 8
dIh8ez10

Oefening: H6-film-default

Leerdoelen

  • methodes met default argumenten

Functionele analyse

Maak een methode FilmRuntime met 3 parameters:

  1. Een string die de naam van de film bevat

  2. Een integer die duur in minuten van de film bevat

  3. Een enum-type FilmGenre die het genre van de film bevat. Deze enum heeft de mogelijke waarden Drama, Action, Comedy en Uncategorized.

Deze methode toont dan een samenvatting van de film op het scherm, gevolgd door zijn duur en genre in volgend formaat:

The Matrix (120 minuten, Action)

Indien de duur niet gespecifieerd wordt, wordt gezegd dat hij 90 minuten duurt. Indien het genre niet wordt meegegeven, wordt "Uncategorized" vermeld op het scherm.

Technische analyse

Schrijf je methode met drie parameters, maar geef de duur en het genre een default waarde. Toon aan in je main dat de methode werkt met zowel 1, 2 als 3 parameters. Toon ook aan dat je met "named arguments" de methode kan aanroepen.

UI

console applicatie

voorbeeldinteractie(s)

The Matrix (120 minuten, Action)
Crouching Tiger, Hidden Dragon (90 minuten, Unknown)

Oefening: H6-rekenmachine

Leerdoelen

  • werken met verschillende methodes

Functionele analyse

Rekenmachine

Maak de methoden TelOp, TrekAf, VermenigVuldig en Deel.

Aan deze methoden worden twee doubles meegeven als parameter. Het returntype is de bewerking met die twee parameters.

Maak een eenvoudig programma'tje waarin je deze methoden test a.h.v. een keuze die gemaakt moet worden.

Maak een keuze uit
1. Optellen
2. Aftrekken
3. Vermenigvuldingen
4. Delen
Geef het overeenstemmende nummer als keuze in: 1

Geef het eerste nummer in: 2
Geef het tweede nummer in: 3

Resultaat: (2 + 3) = 5

Technische analyse

Schrijf je vier methoden, telkens met twee parameters.

Roep één van deze vier methoden aan a.h.v. een switch constructie binnen uw main.

UI

console applicatie

voorbeeldinteractie(s)

TelOp(number1, number2)

Switch

Switch

Een switch statement is een program-flow element om een veelvoorkomende constructie van if/if else...else elementen eenvoudiger te tonen. Vaak komt het voor dat we bijvoorbeeld aan de gebruiker vragen om een keuze te maken (bijvoorbeeld een getal van 1 tot 10, waarbij ieder getal een ander menu-item uitvoert van het programma), zoals:

int option;
Console.WriteLine("Kies 1 voor afbreken, 2 voor opslaan, 3 voor laden:");
option = Convert.ToInt32(Console.ReadLine());

if (option == 1)
    Console.WriteLine("Afbreken gekozen");
else if (option == 2)
    Console.WriteLine("Opslaan gekozen");
else if (option == 3)
    Console.WriteLine("Laden gekozen");
else
    Console.WriteLine("Onbekende keuze");

Met een switch kan dit eenvoudiger. De syntax van een switch is een beetje specialer dan de andere programma flow-elementen (if, while, etc.), namelijk als volgt:

switch (value)
{
      case constant:
           statements
           break;
      case constant:
           statements
           break;
      default:
           statements
           break;
  }

value is de waarde of variabele (beide mogen) die wordt gebruikt als test in de switch. Iedere case begint met het case keyword gevolgd door de waarde die value moet hebben om in deze case te springen. Na het dubbelpunt volgt vervolgens de code die moet uitgevoerd worden in deze case. De case zelf mag eender welke code bevatten (methoden, nieuwe program flow elementen, etc.), maar moet zeker afgesloten worden met het break keyword.

Tijdens de uitvoer zal het programma value vergelijken met iedere case constant van boven naar onder. Wanneer een gelijkheid wordt gevonden dan wordt die case uitgevoerd. Indien geen case wordt gevonden die gelijk is aan value dan zal de code binnen de default-case uitgevoerd worden (de else achteraan indien alle vorige if else-tests negatief waren).

Het menu van zonet kunnen we nu herschrijven naar een switch:

int option;
Console.WriteLine("Kies 1 voor afbreken, 2 voor opslaan, 3 voor laden:");
option = Convert.ToInt32(Console.ReadLine());
switch (option)
{
    case 1:
        Console.WriteLine("Afbreken gekozen");
        break;
    case 2:
        Console.WriteLine("Opslaan gekozen");
        break;
    case 3:
        Console.WriteLine("Laden gekozen");
        break;
    default:
        Console.WriteLine("Onbekende keuze");
        break;
  }

Opgelet:

De case waarden moeten literals zijn. Dit zijn waarden die je letterlijk uitschrijft, d.w.z. die je niet voorstelt als variabele (1, "1", 1.0, 1.d, '1', etc.)

Falthrough

Soms wil je dat dezelfde code uitgevoerd wordt bij 2 of meer cases. Je kan ook zogenaamde fallthrough cases beschrijven wat er als volgt uit ziet:

switch (option)
{
    case 1:
        Console.WriteLine("Afbreken gekozen");
        break;
    case 2:
    case 3:
        Console.WriteLine("Laden of opslaan gekozen");
        break;
    default:
        Console.WriteLine("Onbekende keuze");
        break;
  }

In dit geval zullen zowel de waarden 2 en 3 resulteren in de zin "Laden of opslaan gekozen" op het scherm.

website en app "Code2flow"
casting
Enums gebruiken
pdf
hier
projecteuler.net

While en Do While

While

De syntax van een while loop is eenvoudig:

while (booleaanse expressie) 
{
  // C# die zal uitgevoegd worden zolang de booleaanse expressie waar is
}

Waarbij, net als bij een if statement, de conditie uitgedrukt wordt als een booleaanse expressie met 1 of meerdere relationele operatoren.

Zolang de conditie true is zal de code binnen de accolades uitgevoerd worden. Indien dus de conditie reeds vanaf het begin false is dan zal de code binnen de while-loop niet worden uitgevoerd.

Telkens wanneer het programma aan het einde van het while codeblock komt springt het terug naar de conditie bovenaan en zal de test wederom uitvoeren. Is deze weer true dan wordt de code weer uitgevoerd. Van zodra de test false is zal de code voorbij het codeblock springen en na het while codeblok doorgaan.

Het diagramma is duidelijk:

Een voorbeeld van een eenvoudige while loop:

int myCount = 0;

while (myCount < 100)
{
    myCount++;
    Console.WriteLine(myCount);
}

Zolang myCount kleiner is dan 100 (myCount < 100) zal myCount met 1 verhoogd worden en zal de huidige waarde van myCount getoond worden. We krijgen met dit programma dus alle getallen van 1 tot en met 100 op het scherm onder elkaar te zien.

Daar de test gebeurt aan het begin van de loop wil dit zeggen dat het getal 100 nog wel getoond zal worden. Begrijp je waarom? Test dit zelf!

Do while

In tegenstelling tot een while loop, zal een do-while loop sowieso minstens 1 keer uitgevoerd worden. Ongeacht de opgegeven conditie zal de do-while loop zijn code 1 keer uitvoeren. We herhalen deze zin uitdrukkelijk 2x zodat het verschil tussen beide type loops duidelijk blijft.

Vergelijk volgende diagramma van de do while:

met die hierboven van de while.

De syntax van een do-while is eveneens verraderlijk eenvoudig:

do{
      // C# die uitgevoerd zal worden zolang de booleaanse expressie waar is
} while (booleaanse expressie);

Merk op dat achteraan de conditie een puntkomma na het ronde haakje staat. Dit is een véél voorkomende fout. Bij een while is dit niet! Daar de test van een do-while achteraan de code van de loop gebeurt is het logisch dat een do-while dus minstens 1 keer wordt uitgevoerd. Het volgende eenvoudige aftelprogramma toont de werking van de do-while loop.

int i = 10;
do
{
    i--;
    Console.WriteLine(i);
} while (i > 0);

Begrijp je wat dit programma zal doen?

Complexe condities

Uiteraard mag de conditie waaraan een loop moet voldoen complexer zijn door middel van de relationele operatoren.

Volgende while bijvoorbeeld zal uitgevoerd worden zolang teller groter is dan 5 en de variabele naam van het type string niet gelijk is aan "tim":

while(teller > 5 && naam != "tim")
{
  //Keep repeating
}

Oneindige loops

Indien de loop-conditie nooit false wordt dan heb je een oneindige loop gemaakt. Soms is dit gewenst gedrag (bijvoorbeeld bij de gameloop) soms is dit een bug en zal je dit moeten debuggen.

Volgende twee voorbeelden tonen dit:

  • Een bewust oneindige loop:

    while(true)
    {
    //See you in infinity
    }
  • Een bug die een oneindige loop veroorzaakt:

    int teller = 0; 
    while(teller<10)
    {
    Console.WriteLine(teller);
    teller--;    //oops, dit had teller++ moeten zijn
    }

Probeer er altijd zeker van te zijn dat de variabele(n) die je gebruikt in je test-conditie ook in de loop aangepast worden. Als deze in de loop constant blijft dan zal ook de test-conditie dezelfde blijven en heb je dus een oneindige loop gemaakt.

Scope van variabelen in loops

int teller= 1;
while(teller <= 10)
{
   int som= 0;
   som= som+teller;
   teller++;
}
Console.WriteLine(som); //deze lijn zal fout genereren

De correcte manier om dit op te lossen is te beseffen dat de variabele som enkel binnen de accolades van de while-loop gekend is. Op de koop toe wordt deze steeds terug op 0 gezet en er kan dus geen som van alle teller-waarden bijgehouden worden:

int teller= 1;
int som=0;  
while(teller <= 10)
{

   som= som+teller;
   teller++
}
Console.WriteLine(som);

Foute input van gebruiker met loops verwerken

Dankzij loops kunnen we nu ook eenvoudiger omgaan met foutieve input van de gebruiker. Stel dat we volgende vraag hebben:

Console.WriteLine("Geef uw keuze in: a, b of c");
string input= Console.ReadLine();

Met een loop kunnen we nu deze vragen blijven stellen tot de gebruiker een geldige input geeft:

string input;
do
{
  Console.WriteLine("Geef uw keuze in: a, b of c");
  input= Console.ReadLine();
}while(input != "a"  || input != "b" || input != "c");

Zolang (while) de gebruiker niet "a" OF "b" OF "c" invoert zal de loop zichzelf blijven herhalen.

Merk op dat we de variabele string input VOOR de do while moeten aanmaken. Zouden we die IN de loop pas aanmaken dan zou de variabele niet als test kunnen gebruikt worden aan het einde van de loop.

Je ziet dat het stuk } while(input...); achteraan buiten de accolades van de loop ligt en dus een andere scope heeft.

Kennisclip

Oefeningen deel 2

Probeer niet alle oefeningen met hetzelfde type loop te doen, wissel tussen while, do...while en for.

Indien er sprake is van n in de opgave dan is dit een getal dat je eerst aan de gebruiker moet vragen.

Opgelet: de oplossing van dit soort oefeningen vindt je overal. Weersta hier aan, en probeer ZELF de oplossing te vinden. Dat is de enige manier om dit te leren.

  1. Toon alle natuurlijke getallen van 1 tot n.

  2. Toon alle natuurlijke getallen van n tot 1.

  3. Toon alle even getallen tussen 1 en 100.

  4. Toon alle oneven getallen tussen 1 en 100.

  5. Toon de som van alle getallen van 1 tot n (dus 1+2+3+4+...+n).

  6. Toon de som van alle even getallen van 1 tot n.

  7. Toon de som van alle oneven getallen van 1 tot n.

  8. Schrijf een programma dat het aantal digits in een getal telt (het getal 12348 heeft bijvoorbeeld 5 digits).

  9. (PRO) Schrijf een programma dat een ingevoerd getal als tekst uitschrijft. Als de gebruiker dus 123 invoert zal de uitvoer zijn: honderd drie en twintig.

  10. Schrijf een programma dat alle ascii karakters en hun waarde toont van 10 tot n (tip: char c = Convert.ToChar(65); zal hoofdletter A tonen)

  11. Toon het alfabet van a tot z.

  12. Schrijf een programma dat de macht van een getal toont. De gebruiker voor eerst het getal in, gevolgd door de macht (bv 2 en 4 zal als resultaat 16 geven (2 tot de 4e)).

Oefening: H5-priem-checker

Leerdoelen

  • lussen gebruiken om een resultaat op te bouwen

Functionele analyse

Je krijgt een getal van de gebruiker. Je moet nagaan of dit een priemgetal is, d.w.z. of het precies 2 gehele delers heeft.

Technische analyse

Elk geheel getal vanaf 2 heeft minstens 2 gehele delers: 1 en zichzelf. Als dat de enige delers van het gegeven getal zijn, is het priem. Je kan dus nagaan of een getal een priemgetal is door alle getallen vanaf 1 tot het getal zelf te overlopen en na te gaan of deze delers zijn van het getal. (Eigenlijk volstaat het minder getallen te checken maar daar draait het hier niet om.)

UI

console applicatie

voorbeeldinteractie(s)

Geef een getal
> 1
Het getal is geen priemgetal!
Geef een getal
> 2
Het getal is een priemgetal!
Geef een getal
> 6
Het getal is geen priemgetal!
Geef een getal
> 7
Het getal is een priemgetal!

Oefening: H5-priem-generator

Leerdoelen

  • geneste lussen gebruiken om een meerdere resultaten te laten zien

Functionele analyse

Je krijgt een getal van de gebruiker. Je moet alle priemgetallen kleiner of gelijk aan dit getal laten zien.

Technische analyse

Je kan het idee uit de vorige oefening herbruiken, maar nu zijn de getallen die je controleert niet afgeleverd door de gebruiker. Je moet ze zelf genereren in een for-lus. Als je in de vorige oefening een for-lus hebt gebruikt, zal je dus twee for-lussen moeten nesten.

UI

console applicatie

voorbeeldinteractie(s)

Geef een getal
> 11
Alle priemgetallen kleiner dan of gelijk aan 11 zijn:
2
3
5
7
11

Oefeningen

H4-BMI-if

Leerdoelen

  • conditionele boodschappen

Functionele analyse

Deze opgave bouwt verder op H3-BMI-berekenaar. Meerbepaald moet je de gebruiker niet alleen zijn of haar BMI tonen, maar moet je ook een gekleurde boodschap tonen die laat weten of de BMI goed zit of niet.

Voor een BMI lager dan 18,5 toon je de boodschap "ondergewicht" in rode tekst. Voor een BMI die hoger ligt dan 18,5 maar lager dan 25, toon je de boodschap "normaal gewicht" in groene tekst. Voor een hogere BMI, maar lager dan 30, toon je in gele tekst "overgewicht". Voor een hogere BMI, maar lager dan 40, toon je "zwaarlijvig" in rode tekst. Voor een hogere BMI toon je "ernstige obesitas" in magenta.

Technische analyse

Via if en else (en dus ook else if) kan je gevallen onderscheiden. Gebruik ConsoleColor.Red, ConsoleColor.Green, ConsoleColor.Yellow en ConsoleColor.Magenta.

UI

console applicatie

voorbeeldinteractie(s)

Hoeveel weeg je in kg?
> 69.0
Hoe groot ben je in m?
> 1.78
Je BMI bedraagt 21.78.
normaal gewicht

De tekst zou in het groen moeten verschijnen maar Gitbook staat dit niet meteen toe.

H4-schoenverkoper

Leerdoelen

  • conditionele berekeningen

Functionele analyse

Maak een programma dat aan de gebruiker vraagt hoeveel paar schoenen hij wenst te kopen. Ieder paar schoenen kost normaal 20 euro. Indien de gebruiker 10 paar of meer koopt, kost elk paar maar 10 euro. Toon aan de gebruiker de totale prijs.

Breid in een tweede stap je programma uit zodat gevraagd wordt vanaf welk aantal schoenen de prijs daalt naar 10 euro.

Technische analyse

Hou variabelen bij voor de prijs, de gereduceerde prijs en het aantal paar dat nodig is om korting te krijgen. De eerste twee variabelen maak je const.

UI

console applicatie

voorbeeldinteractie(s)

Hoeveel paar schoenen wil je kopen?
> 3
Je moet 60 euro betalen.
Hoeveel paar schoenen wil je kopen?
> 12
Je moet 120 euro betalen.

(Na de uitbreiding)

Vanaf welk aantal geldt de korting?
> 7
Hoeveel paar schoenen wil je kopen?
> 8
Je moet 80 euro betalen.

H4-Ohm-berekenaar

Leerdoelen

  • conditionele berekeningen

De wet van Ohm houdt in dat een elektrische stroom (voorgesteld als I) gelijk is aan een spanningsverschil (U) gedeeld door een weerstand (R), dus I = U / R.

Functionele analyse

Vraag aan de gebruiker wat hij wenst te berekenen: Spanning, Weerstand of Stroomsterkte. Vraag vervolgens de twee andere waarden (als dus de gebruiker "Spanning" kiest vraag je aan de gebruiker de stroomsterkte en de weerstand) en bereken m.b.v. de wet van Ohm de gewenste waarde en toon aan de gebruiker.

Technische analyse

Denk eraan dat de gegeven formule wiskundig gedefinieerd is. In C♯ zal je rekening moeten houden met het feit dat deze drie maten uitgedrukt kunnen worden in kommagetallen.

Je mag hier gewoon strings gebruiken om na te gaan welke maat de gebruiker heeft ingetypt. Je mag veronderstellen dat de getallen uitgedrukt zijn in de gewoonlijke eenheden (volt, ampère, ohm) zodat je ze gewoon kan invullen in de formule.

UI

console applicatie

voorbeeldinteractie(s)

Wat wil je berekenen? spanning, weerstand of stroomsterkte?
> stroomsterkte
Wat is de spanning?
> 30
Wat is de weerstand?
> 20
De stroomsterkte bedraagt 1.5.

H4-Schrikkeljaar

Leerdoelen

  • conditionele berekeningen

  • geneste condities

Functionele analyse

De gebruiker voert een jaartal in en jouw programma toont of het wel of geen schrikkeljaar is. Een schrikkeljaar is deelbaar door 4, behalve als het ook deelbaar is door 100, tenzij het wél deelbaar is door 400.

Technische analyse

  • gebruik de modulo-operator (%) om deelbaarheid door 4 na te gaan

  • gebruik een constructie met geneste ifs (en elses) om alle gevallen af te handelen

UI

console applicatie

voorbeeldinteractie(s)

> 1997
geen schrikkeljaar
> 1996
schrikkeljaar
> 1900
geen schrikkeljaar
> 2000
schrikkeljaar

H4-kleurcodes

Leerdoelen

  • conditionele berekeningen

  • werken met switch

Functionele analyse

Deze oefening bouwt voort op H2-weerstandberekenaar-deel1. Vraag nu aan de gebruiker om de ringkleuren van de eerste 3 ringen in te voeren als tekst (bv groen). Toon vervolgens de de waarde van deze weerstand.

Technische analyse

Je zal elke kleur moeten omzetten in een getal en dan je eerdere oplossing hergebruiken. Omzetten doe je door de ingevoerde tekst te vergelijken met een vaste string en naargelang het resultaat variabelen voor ring 1, 2 en 3 in te vullen. Los deze oefening op met switch!

UI

console applicatie

voorbeeldinteractie(s)

Wat is de kleur van de eerste ring?
> rood
Wat is de kleur van de tweede ring?
> paars
Wat is de kleur van de derde ring?
> rood
Deze weerstand heeft waarde van 2700 Ohm

Voel je je vandaag extra leergierig? Maak dan een extra enum, ResistorColors en zet de ingegeven tekst om naar waarden binnen deze enum vooraleer je de berekening uitvoert.

H4-orakeltje

Leerdoelen

  • conditionele berekeningen

  • werken met switch

  • werken met enumeraties

Functionele analyse

Technische analyse

  • Je mag veronderstellen dat de huidige leeftijd onder het theoretische maximum ligt.

  • Gebruik een enum, met als naam Sexes en als waarden Male en Female om de geslachten voor te stellen.

    • Het programma zou in dit geval misschien iets simpeler zijn zonder, maar dan gebruik je dit een eerste keer.

  • Je kan vermijden dat de voorspelde leeftijd te hoog gaat door je .Next-call aan te passen, zodat de hoogst mogelijke waarde diegene is waarbij je de maximale leeftijd voor het gegeven geslacht bereikt.

UI

console applicatie

voorbeeldinteractie(s)

Wat is je geslacht?
> m
Hoe oud ben je?
> 32
Je hebt nog 80 jaar te leven!

H4-ruimte-specifiek

Dit is een complexere oefening dan de vorige! Pak het stapje voor stapje aan en gebruik indien nodig de debugger.

Leerdoelen

  • werken met enumeraties

  • conversie van enums van en naar getallen

  • werken met switch

Functionele analyse

Deze opgave bouwt verder op H1-ruimte. Eerst vraag je de gebruiker om zijn of haar gewicht in te voeren. Daarna geef je een lijst van de planeten in ons zonnestelsel (Pluto inbegrepen, ook al is dat officieel geen planeet). Iedere planeet wordt voorafgegaan door een nummer. Dan selecteert de gebruiker het nummer van een van deze planeten en ten slotte toont het programma hoe veel de persoon weegt op de planeet in kwestie.

Technische analyse

Je hebt hier verschillende zaken nodig:

  • conversie naar een double om een gewicht in te lezen

  • een enum om de planeten voor te stellen

  • conversie van de planeten naar getallen om de gebruiker een nummer voor elke planeet te tonen

  • conversie in de omgekeerde richting om de keuze van de gebruiker te verstaan

  • een switch om de juiste vermenigvuldigingsfactor te bepalen

UI

console applicatie

voorbeeldinteractie(s)

Hoeveel weeg je?
> 69.0
Voor welke planeet wil je je gewicht kennen?
1. Mercurius
2. Venus
3. Aarde
4. Mars
5. Jupiter
6. Saturnus
7. Uranus
8. Neptunus
9. Pluto
> 2
Daar weeg je 62.79kg.

For

For

Een veelvoorkomende manier van while-loops gebruiken is waarbij je een bepaalde teller bijhoudt die je telkens met een bepaalde waarde verhoogt. Wanneer de teller een bepaalde waarde bereikt moet de loop afgesloten worden.

Bijvoorbeeld volgende code om alle even getallen van 0 tot 10 te tonen:

int k = 0;
while(k<11)
{
    Console.WriteLine(k);
    k = k + 2;
}

Met een for-loop kunnen we deze veel voorkomende code-constructie verkort schrijven.

For syntax

De syntax van een for-loop is de volgende:

for (setup; finish test; update)
{
    // C# die zal uitgevoerd worden zolang de finish test true geeft
}
  • setup: In het setup gedeelte zetten we de "wachter-variabele" op de begin waarde. De wachter-variabele is de variabele die we tijdens de loop in het oog zullen houden en die zal bepalen hoe vaak de loop moet uitgevoerd worden.

  • finish test: Hier plaatsen we een booleaanse expressie die de wachter-variabele uit de setup gebruikt om te testen of de loop-code moet uitgevoerd worden.

  • update: Hier plaatsen we wat er moet gebeuren telkens de loop z'n codeblock heeft uitgevoerd. Meestal zullen we hier de wachter-variabele verhogen of verlagen.

Gebruiken we deze kennis nu, dan kunnen we de eerder vermelde code om de even getallen van 0 tot en met 10 tonen als volgt:

for (int i = 0; i < 11; i += 2)
{
    Console.WriteLine(i);
}

Voor de setup-variabele kiest men meestal i, maar dat is niet noodzakelijk. In de setup wordt dus een variabele op een start-waarde gezet. De finish test zal aan de start van iedere loop kijken of de finish test nog waar is, indien dat het geval is dan wordt een nieuwe loop gestart en wordt i met een bepaalde waarde, zoals in update aangegeven, verhoogd.

for-tab-tab

Als je in Visual Studio for typt en dan tweemaal op [tab] duwt krijg je kant en klare for-loop code.

Break

Je kan loops (alle types) altijd vroegtijdig stopzetten door het break keyword. Het gebruik hiervan ligt soms in de schemerzone van misbruik. Probeer dus eerst je probleem anders op te lossen voor je break begint te gebruiken.

Om iemand op StackOverflow te quoten:

When used at the start of a block, as first checks made, they act like preconditions, so it's good. When used in the middle of the block, with some code around, they act like hidden traps, so it's bad."

Kennisclip

Nesting

Nested loops

Wanneer we 1 of meerdere loops in een andere loop plaatsen dan spreken we over geneste loops. Geneste loops komen vaak voor, maar zijn wel een ras apart wanneer je deze zaken wilt debuggen en correct schrijven.

We spreken steeds over de outer loop als de omhullende of "grootste" loop. Waarbij de binnenste loops de inner loop(s) zijn.

Volgende code toont bijvoorbeeld 2 loops die genest werden:

De uitvoer hiervan zal als volgt zijn:

Begrijp je hoe we aan deze uitvoer komen? (tip: analyseer de inner en outer loop apart)

Geneste loops tellen

Om te tellen hoe vaak de 'inner' code zal uitgevoerd worden dien je te weten hoe vaak iedere loop afzonderlijk wordt uitgevoerd. Vervolgens vermenenigvuldig je al deze getallen met elkaar.

Een voorbeeld: Hoe vaak zal het woord Hallo op het scherm verschijnen bij volgende code?

De outer loop zal 10 maal uitgevoerd worden (i zal de waarden 0 tot en met 9 krijgen). De inner loop zal 5 maal (j zal de waarden 0 tot en met 4 krijgen) uitgevoerd worden. In totaal zal dus 50 maal Hallo op het scherm verschijnen (5x10).

Break in nested loop

Let er op dat break je enkel uit de huidge loop zal halen. Indien je dit dus gebruik in de inner loop dan zal de outer loop nog steeds voortgaan. Nog een reden om zéér voorzichtig om te gaan in het gebruik van break.

Bibliotheken

Je herkent een methode aan de ronde haakjes na de methodenaam. Je hebt dus reeds een aantal methoden gebruikt zonder dat je het wist, denk maar aan WriteLine(), ReadLine() en Parse().

Dit zijn dus alle 3 methoden: stukken code die een specifieke taak uitvoeren.

Sommige methoden, zoals WriteLine(), vereisen dat je een aantal parameters meegeeft. De parameters dien je tussen de ronde haakjes te zetten. Hierbij is het uiterst belangrijk dat je de volgorde respecteert die de ontwikkelaar van de methode heeft gebruikt. Indien je niet weet wat deze volgorde is kan je altijd Intellisense gebruiken. Typ gewoon de methode in je code en stop met typen na het eerste ronde haakje, vervolgens verschijnen alle mogelijke manieren waarop je deze methoden kan oproepen.

Tussen de haakjes zien we welke parameters en hun type je mag meegeven aan de methode, gevolgd door het return-type van de methode en een eventuele beschrijving (merk dus op dat je de WriteLine-methode ook mag aanroepen zonder parameters, dit zal resulteren in een lege lijn in de console).

Met behulp van de F1-toets kunnen meer info over de methode in kwestie tonen. Hiervoor dien je je cursor op de Methode in je code te plaatsen, en vervolgens op F1 te drukken.

Voor WriteLine geeft dit:

In de overload list zien we de verschillende manieren waarop je de methode in kwestie kan aanroepen. Je kan op iedere methode klikken voor meer informatie en een codevoorbeeld.

Intellisense

"Hoe kan je deze methode nu gebruiken?" is een veelgestelde vraag. Zeker wanneer je de basis van C# onder knie hebt en je stilletjes aan met bestaande .NET bibliotheken wil gaan werken. Wat volgt is een essentieel onderdeel van VS dat veel gevloek en tandengeknars zal voorkomen.

De help-files van VS zijn zeer uitgebreid en dankzij IntelliSense krijg je ook aardig wat informatie tijdens het typen van de code zelf.

Type daarom onder vorige WriteLine-zin het volgende:

Wacht nu even en er zal na het punt (.) een lijst komen van methoden en fields die beschikbaar zijn. Je kan hier met de muis doorheen scrollen en zo zien welke methoden allemaal bij de Console klasse horen.

Scroll maar eens naar de WriteLine-methode en klik er op. Je krijgt dan extra informatie over die methode te zien:

Doe hetzelfde voor de ReadLine methode:

Je ziet bovenaan string Console.ReadLine() staan. Bij de WriteLine stond er void Console.WriteLine(). Die void wil zeggen dat de methode WriteLine niets terugstuurt. In tegenstelling tot ReadLine dat een string teruggeeft. Indien de methode één of meerdere parameters vereist dan zullen deze hier ook getoond worden:

De Math.Pow methode vereist dus bijvoorbeeld 2 parameters van het type double. Wanneer je nu begint te typen dan zal intellisense tonen waarvoor iedere parameter staat wanneer je aan die parameter gaat beginnen typen:

Let er op dat de van variabelen bij loops zeer belangrijk is. Indien je een variabelen binnen de loop definieert dan zal deze steeds terug "gereset" worden wanneer de volgende cyclus van de loop start. Volgende code toont bijvoorbeeld foutief hoe je de som van de eerste 10 getallen (1+2+3+...+10) zou maken:

Een extra grote hoop oefeningen om je loops te drillen (). De oefeningen zijn gerangschikt naar moeilijkheid, je moet deze allemaal met loops oplossen! Hoe ver geraak je?

Schrijf een programma een getal n ontbindt in . Factoren zijn de getallen waardoor je n kan delen zonder rest (van bijvoorbeeld het getal 100 zijn de factoren 1,2,4,5,10,20,25,50,100 ).

Toon de reeks van tot n termen.

Vul de oefening aan uit het vorige hoofdstuk (zie ). Voor het orakel je vertelt hoe lang je te leven hebt zal eerst vragen naar je geslacht, dat je ingeeft als v of m. Dan vraagt ze je leeftijd. Mannen leven maximum tot hun 120 jaar. Vrouwen tot 150 jaar. Het orakel moet rekening houden met je huidige leeftijd, dus het mag niet zeggen dan een man nog 110 jaar te leven heeft als hij al 50 is, want dan zou hij ouder worden dan 120.

Lees zeker want er zijn nog enkele subtiliteiten in for-loops die we hier niet behandelen.

Lees meer over het gebruik van break .

scope
De while loop en soorten loops
Do-while loops
originele bron
factoren
Fibonacci
deze pagina
deze for tutorial na
Bron StackOverFlow: Are break and continue bad programming practices?
hier
De for loop
int tellerA= 0;
int tellerB= 0;

while(tellerA < 3 )  //outer loop
{
    tellerA++;
    tellerB = 0;
    while(tellerB < 5)
    {
        tellerB++;
        Console.WriteLine($"Teller:{tellerA}, Teller2:{tellerB}")
    }
}
for (int i = 0; i < 10; i++)
{
    for (int j = 0; j < 5; j++)
    {
        Console.WriteLine("Hallo");
    }
}
System.Console.

Array principes

Arrays

Arrays zijn een veelgebruikt principe in vele programmeertalen. Het grote voordeel van arrays is dat je een enkele variabele kunt hebben die een grote groep waarden voorstelt van eenzelfde type. Hierdoor wordt je code leesbaarder en eenvoudiger in onderhoud. Arrays zijn een zeer krachtig hulpmiddel, maar er zitten wel enkele venijnige addertjes onder het gras.

Een array is niet meer dan een verzameling waarden van hetzelfde type (bijvoorbeeld een verzameling ints, doubles of chars). Deze waarden kunnen benaderd worden via 1 enkele variabele, de array zelf. Door middel van een index kan ieder afzonderlijk element uit de array aangepast of uitgelezen worden.

Nut van arrays

Stel dat je de dagelijkse neerslag wenst te bewaren. Dit kan je zonder arrays eenvoudig:

int dag1= 34;
int dag2= 45;
int dag3= 0;
int dag4= 34;
int dag5= 12;
int dag6= 0;
int dag7= 23;

Maar wat als je plots de neerslag van een heel jaar, 365 dagen, wenst te bewaren. Of een hele eeuw? Van zodra je een bepaalde soort data hebt die je veelvuldig wenst te bewaren dan zijn arrays de oplossing.

Bron

Grote delen van dit hoofdstuk zijn vertaald uit het handboek C# 4.0 Essentials.

Array Basics

Arrays declareren

Een array creëren (declareren) kan op verschillende manieren. Hoewel manier 1 de meest gebruikelijke is, zal deze voor de beginnende programmeur nog wat abstract lijken vanwege het gebruik van het new keyword. Manier 2 is de eenvoudigste en snelste manier, maar deze is wel minder flexibel.

Manier 1

De eenvoudigste variant is deze waarbij je een array variabele aanmaakt, maar deze nog niet initialiseert (i.e. je maakt enkel een identifier in aan). De syntax is als volgt:

type[] arraynaam;

Type kan dus eender welk type zijn dat je reeds kent. De [ ] (square brackets) duiden aan dat het om een array gaat.

Voorbeelden van array declaraties kunnen dus bijvoorbeeld zijn:

    int[] verkoopCijfers;
    double[] gewichtHuisdieren;
    bool[] examenAntwoorden;

Stel dat je dus een array van strings wenst waarin je verschillende kleuren zal plaatsen dan schrijf je:

string[] myColors;

Vervolgens kunnen we later waarden toekennen aan de array, hiervoor gebruiken we het new sleutelwoord.

string[] myColors;
myColors = new string[] {"red", "green", "yellow", "orange", "blue"};

Je array zal vanaf dit punt een lengte van 5 hebben en kan niet meer groeien.

Manier 2

Indien je direct waarden wilt toekennen (initialiseren) tijdens het aanmaken van de array zelf dan mag dit ook als volgt:

string[] myColors = {"red", "green", "yellow", "orange", "blue"};

Ook hier zal dus vanaf dit punt je array een vaste lengte van 5 elementen hebben. Merk op dat deze manier dus enkel werkt indien je reeds weet welke waarden in de array moeten. In manier 1 kunnen we perfect een array aanmaken en pas veel later in programma ook effectief waarden toekennen (bijvoorbeeld door ze stuk per stuk door een gebruiker te laten invoeren).

Manier 3

Nog een andere manier om arrays aan te maken is de volgende, waarbij je aangeeft hoe groot de array moet zijn, zonder reeds effectief waarden toe te kennen:

string[] myColors;
myColors = new string[5];

Samengevat

De 3 manieren om arrays te declareren zijn dus:

//Manier 1
string[] myColors;
myColors = new string[] {"red", "green", "yellow", "orange", "blue"};
//Manier 2
string[] myColors = {"red", "green", "yellow", "orange", "blue"};
//Manier 3
string[] myColors;
myColors = new string[5];

Elementen van een array aanpassen en uitlezen

Van zodra er waarden in een array staan of moeten bijgeplaatst worden dan kan je deze benaderen met de zogenaamde array accessor notatie Deze notatie is heel eenvoudigweg de volgende:

myColors[i];

We plaatsen de naam van de array, gevolgd door brackets waarbinnen een getal i aangeeft het hoeveelste element we wensen te benaderen (lezen en/of schrijven).

De index van een C#-array start steeds bij 0. Indien je dus een array aanmaakt met lengte 10 dan heb je de indices 0 tot en met 9.

Veelgemaakte fouten: Lengte en indexering van arrays

Het gebeurt vaak dat beginnende programmeurs verward geraken omtrent het aanmaken van een array aan de hand van de lengte en het indexeren.

De regels zijn duidelijk:

  • Bij het maken van een array is de lengte van een array gelijk aan het aantal elementen dat er in aanwezig is. Dus een array met 5 elementen heeft als lengte 5.

  • Bij het schrijven en lezen van individuele elementen uit de array (zie hierna) gebruiken we een indexering die start bij 0. Bijgevolg is de index 4 van het laatste elemente in een array met lengte 5.

Schrijven

Ook schrijven van waarden naar de array gebruikt dezelfde notatie. Enkel moet je dus deze keer de array accessor-notatie links van de toekenningsoperator plaatsen. Stel dat we bijvoorbeeld de waarde van het eerste element uit de myColors array willen veranderen van red naar indigo, dan gebruiken we volgende notatie:

myColors[0] = "indigo";

Als we dus bij aanvang nog niet weten welke waarden de individuele elementen moeten hebben in een array, dan kunnen we deze eerst definiëren, en vervolgens individueel toekennen:

string[] myColors;
myColors = new string[5];
// ...
myColors[0] = "red";
myColors[1] = "green";
myColors[2] = "yellow";
myColors[3] = "orange";
myColors[4] = "blue";

Uitlezen

Stel dat we een array aanmaken (eerste lijn) dan kunnen we dus bijvoorbeeld het getal 90 op het scherm tonen als volgt:

int[] scores = {100, 90, 55, 0, 34};
int kopie = scores[1];
Console.WriteLine(kopie);

of nog korter:

int[] scores = {100, 90, 55, 0, 34};
Console.WriteLine(scores[1]);

Stel dat we een array van getallen hebben, dan kunnen we dus bijvoorbeeld 2 waarden uit die array optellen en opslaan in een andere variabele als volgt:

int[] numbers = {5, 10, 30, 45};
int som = numbers[0] + numbers[1];

De variabele som zal dan vervolgens de waarde 15 bevatten (5+10).

Stel dat we alle elementen uit de array numbers met 5 willen verhogen, we kunnen dan schrijven:

int[] numbers = {5, 10, 30, 45};
numbers[0] += 5;
numbers[1] += 5;
numbers[2] += 5;
numbers[3] += 5;

Nog beter is het natuurlijk deze code (die 4 keer quasi dezelfde statement bevat) te vereenvoudigen tot:

int[] numbers = {5, 10, 30, 45};
int teller = 0;
while (teller < 4)
{
    numbers[teller] += 5;
    teller++
}

Of het equivalent met een for-loop:

int[] numbers = {5, 10, 30, 45};
for(int teller = 0; teller < 4; teller++)
{
    numbers[teller] += 5;
}

De lengte van de array te weten komen

Soms kan het nodig zijn dat je in een later stadium van je programma de lengte van je array nodig hebt. De Length eigenschap van iedere array geeft dit weer. Volgend voorbeeld toen dit:

string[] myColors = {"red", "green", "yellow", "orange", "blue"};
Console.WriteLine("Length of array = {0}", myColors.Length);

De variabele myColors.Length is een special element, van het type int, die iedere array met zich meedraagt (zie volgende semester). Je kan dus deze lengte ook toekennen aan een variabele:

int arrayLength = myColors.Length;

De Length-property wordt vaak gebruikt in for/while loops waarmee je de hele array wenst te doorlopen. Door de Length-property te gebruiken als grenscontrole verzekeren we er ons van dat we nooit buiten de grenzen van de array zullen lezen of schrijven:

//Alle elementen van een array tonen
for (int i = 0; i < getallen.Length; i++)
{
    Console.WriteLine(getallen[i]);
}

Volledig voorbeeldprogramma met arrays

Met al de voorgaande informatie is het nu mogelijk om heel eenvoudig complexere programma's te schrijven die veel data moeten kunnen verwerken. Meestal gebruikt men een for-element om een bepaalde operatie over de hele array toe te passen.

Het volgende programma zal een array van integers aanmaken die alle gehele getallen van 0 tot 99 bevat. Vervolgens zal ieder getal met 3 vermenigvuldigd worden. Finaal tonen we tonen we enkel die getallen die een veelvoud van 4 zijn na de bewerking.

//Array aanmaken
int[] getallen = new int[100];

//Array vullen
for (int i = 0; i < getallen.Length; i++)
{
    getallen[i] = i;
}

//Alle elementen met 3 vermenigvuldigen
for (int i = 0; i < getallen.Length; i++)
{
    getallen[i] = getallen[i] * 3;
}

//Enkel veelvouden van 4 op het scherm tonen
for (int i = 0; i < getallen.Length; i++)
{
    if(getallen[i] % 4 == 0)
        Console.WriteLine(getallen[i]);
}

Geheugengebruik bij arrays

Arrays kopiëren

Arrays worden 'by reference' gebruikt in C#. Het gevolg hiervan is dat volgende code niet zal doen wat je wenst (ploegen, nieuwePloegen zijn twee arrays van bijvoorbeeld een string[]).

nieuwePloegen = ploegen;

Deze code zal perfect werken. Wat er er echter is gebeurd is dat we de referentie naar ploegen ook in nieuwePloegen hebben geplaatst. Bijgevolg verwijzen beide variabelen naar dezelfde array, namelijk die waar ploegen al naar verwees. We hebben een soort alias gemaakt en kunnen nu op twee manieren de array benaderen. Als je dus schrijft:

nieuwePloegen[4] = "Beerschot";

Dan is dat hetzelfde als schrijven:

ploegen[4] = "Beerschot";

En waar staan de ploegen in de nieuwePloegen array? Die bestaat niet meer!

Wil je dus arrays kopieren dan kan dat niet op deze manier: je moet manueel ieder element van de ene naar de andere array kopiëren als volgt:

for(int i = 0; i < ploegen.Length; i++)
{
    nieuwePloegen[i] = ploegen[i];
}

Uitbreidingsoefeningen

Film Default

Maak een methode FilmRuntime() die 3 parameters accepteert:

  1. Een string die de naam van de film bevat

  2. Een integer die duur in minuten van de film bevat

  3. Een enum-type die het genre van de film bevat (Drama, Actie, etc.)

Indien de duur van de film niet wordt meegeven wordt een standaard lengte van 90 minuten ingesteld. Indien het genre niet wordt meegeven dan wordt deze default op Onbekend ingesteld.

De methode geeft niets terug maar toont eenvoudigweg de film op het scherm, gevolgd door z’n duur en genre in volgende formaat.

The Matrix (120minuten, Actie)

Toon aan in je main dat de methode werkt met zowel 1, 2 als 3 parameters. Toon ook aan dat je met ‘named arguments’ de methode kan aanroepen.

Opwarmers met geavanceerde methoden

Zorg ervoor dat de opwarmers uit Deel 0 oefeningen hiervoor steeds minstens 1 optionele parameter hebben. Roep deze methoden aan via named parameters.

Oude oefeningen leesbaarder maken

Kan je code uit vorige hoofdstukken herbruiken door deze in handige methoden te plaatsen zodat je code leesbaarder én bruikbaarder wordt?

Einde deel 1 test herschrijven

Kan je deze code leesbaarder maken door methoden (en loops) toe te voegen?

Verhaalgenerator

Methoden intro

Methoden

Veel code die we hebben geschreven wordt meerdere keren, al dan niet op verschillende plaatsen, gebruikt. Dit verhoogt natuurlijk de foutgevoeligheid. Door het gebruik van methodes kunnen we de foutgevoeligheid van de code verlagen omdat de code maar op 1 plek staat én maar 1 keer dient geschreven te worden. Echter, ook de leesbaarheid en dus onderhoudbaarheid van de code wordt verhoogd.

Wat is een methode

Een methode, ook vaak functie genoemd, is in C# een stuk code ('block') bestaande uit een 0, 1 of meerdere statements. De methode kan herhaaldelijk opgeroepen worden, al dan niet met extra parameters, en kan ook een resultaat terug geven.

De basis-syntax van een methode is de volgende indien je een methode in je hoofdprogramma wenst te schrijven (de werking van het keyword static zien we later):

static returntype MethodeNaam(parameters)
{
    //code van methode
}

Vervolgens kan je deze methode elders oproepen als volgt, indien de methode geen parameters vereist:

MethodeNaam();

Indien er wel parameters nodig zijn dan geef je die mee als volgt, het is belangrijk dat de volgorde van de parameters gehanteerd wordt zoals je in de methode zelf hebt beschreven.

MethodeNaam(parameter1, parameter2, …);

Returntypes

Het returntype van een methode geeft aan wat het type is van de data die de methode als resultaat teruggeeft bij het beëindigen ervan. Eender welk type dat je kent kan hiervoor gebruikt worden, zoals int, string, char, float, etc. Maar ook klassen (zie later) zoals Student, Canvas, etc.

Het is belangrijk dat in je methode het resultaat ook effectief wordt teruggegeven, dit doe je met het keyword return gevolgd door de variabele die moet teruggeven worden. Denk er dus aan dat deze variabele van het type is dat je hebt opgegeven als zijnde het returntype. Van zodra je return gebruikt zal je op die plek uit de methode 'vliegen'.

Volgend voorbeeld bestaat uit een methode die de naam van de auteur van je programma teruggeeft:

static string GetNameAuthor()
{
    string name = "Tim Dams";

    return name;
}

Mogelijke manieren om deze methode in je programma te gebruiken zouden kunnen zijn:

string myName = GetNameAuthor();

Of bijvoorbeeld ook:

Console.WriteLine("This program is written by "+ GetNameAuthor());

Hier een voorbeeld van een methode die de faculteit van 5 berekent. De oproep van de methode gebeurt vanuit de Main-methode:

partial class Program
{
    static int FaculteitVan5()
    {
        int resultaat = 1;
        for (int i = 1; i <= 5; i++)
        {
            resultaat *= i;
        }
        return resultaat;
    }

    static void Main(string[] args)
    {
       Console.WriteLine("Faculteit van 5 is {0}", FaculteitVan5());
    }
}

Void returntype

Indien je methode niets teruggeeft wanneer de methode eindigt (bijvoorbeeld indien de methode enkel tekst op het scherm toont) dan dien je dit ook aan te geven. Hiervoor gebruik je het keyword void. Een voorbeeld:

static void ShowProgramVersion()
{
    Console.Write("The version of this program is: ");
    Console.Write(2.16 + "\n");
}

Parameters doorgeven

Parameters kunnen op 2 manieren worden doorgegeven aan een methode:

  1. Wanneer een parameter by value wordt meegegeven aan een methode, dan wordt een kopie gemaakt van de huidige waarde die wordt meegegeven.

  2. Wanneer echter een parameter by reference wordt meegegeven dan zal een pointer worden meegegeven aan de methode. Deze pointer bevat het adres van de eigenlijke variabele die we meegeven. Aanpassingen aan de parameters zullen daardoor ook zichtbaar zijn binnen de scope van de originele variabele.

Parameters doorgeven by value

Je methode definitie kan ook 1 of meerdere parameters bevatten. Hierbij gebruik je volgende syntax:

static returntype MethodeNaam(type parameter1, type parameter2)
{
    //code van methode
}

Deze parameters zijn nu beschikbaar binnen de methode om mee te werken naar believen.

Stel bijvoorbeeld dat we onze FaculteitVan5 willen veralgemenen naar een methode die voor alle getallen werkt, dan zou je volgende methode kunnen schrijven:

static int BerekenFaculteit(int grens)
{
    int resultaat = 1;
    for (int i = 1; i <= grens; i++)
    {
        resultaat *= i;
    }
    return resultaat;
}
static void Main(string[] args)
{
    int getal = 5;
    Console.WriteLine("Faculteit van {0} is {1}", getal, BerekenFaculteit(getal));
}

Dit geeft als uitvoer: Faculteit van 5 is 120.

Je zou nu echter de waarde van getal kunnen aanpassen (door bijvoorbeeld aan de gebruiker te vragen welke faculteit moet berekend worden) en je code zal nog steeds werken.

Stel bijvoorbeeld dat je de faculteiten wenst te kennen van alle getallen tussen 1 en 10, dan zou je schrijven:

for (int i = 1; i < 11; i++)
{
    Console.WriteLine("Faculteit van {0} is {1}", i, BerekenFaculteit(i));  
}

Dit zal als resultaat geven

Faculteit van 1 is 1
Faculteit van 2 is 2
Faculteit van 3 is 6
Faculteit van 4 is 24
Faculteit van 5 is 120
Faculteit van 6 is 720
Faculteit van 7 is 5040
Faculteit van 8 is 40320
Faculteit van 9 is 362880
Faculteit van 10 is 3628800

Merk dus op dat dankzij je methode, je véél code maar één keer moet schrijven, wat de kans op fouten verlaagt.

Volgorde van parameters

De volgorde waarin je je parameters meegeeft bij de aanroep van een methode is belangrijk. De eerste variabele wordt aan de eerste parameter toegekend, en zo voort.

Het volgende voorbeeld toont dit. Stel dat je een methode hebt:

static void ToonDeling(double teller, double noemer)
{
    string result= Convert.ToString(teller/noemer);
    Console.WriteLine(teller/noemer);
}

Stel dat we nu in onze main volgende aanroep doen:

double n= 4.2;
double t= 5.2;
ToonDeling(n, t);

Dit zal een ander resultaat geven dan wanneer we volgende code zouden uitvoeren:

ToonDeling(t, n);

Ook de volgorde is belangrijk zeker wanneer je met verschillende types als parameters werkt:

static void ToonInfo(string name, int age)
{
   Console.WriteLine($"{name} is {age} old");
}

Deze aanroep is correct:

ToonInfo("Tim", 37);

Deze is FOUT en zal niet compileren:

ToonInfo(37, "Tim");

Commentaar toevoegen

Het is aan te raden om steeds boven een methode een Block-commentaar te plaatsen als volgt (dit werkt enkel bij methoden): ///

Visual Studio zal dan automatisch de parameters verwerken van je methode zodat je vervolgens enkel nog het doel van iedere parameter moet plaatsen.

Stel dat we een methode hebben geschreven die de macht van een getal berekent. We zouden dan volgende commentaar toevoegen:

/// <summary>
/// Berekent de macht van een getal.
/// </summary>
/// <param name="grondtal">Het getal dat je tot een bepaalde macht wilt verheffen</param>
/// <param name="exponent">De exponent van de macht</param>
/// <returns></returns>
static int Macht(int grondtal, int exponent)
{
    int result = grondtal;
    for (int i = 1; i < exponent; i++)
    {
        result *= grondtal;
    }
    return result;
}

Wanneer we nu elders de methode Macht gebruiken dan krijgen we automatische extra informatie:

Kennisclip

Een eenvoudig voorbeeld (bron: handboek Visual C# 2008, Dirk Louis) waar het gebruik van methoden onmiddellijk duidelijk wordt. Stel, je hebt 15000 euro op een spaarrekening vastgezet waarvoor de bank u een rente geeft van 3,5%. Nu wil je natuurlijk weten hoe je kapitaal van jaar tot jaar groeit. Stel dat je aan de verleiding weerstaat en de jaarlijkse rente niet opneemt, maar op de spaarrekening laat staan. Je berekent dan je kapitaal na n jaren met de volgende formule:

eindkapitaal = startkapitaal x (1 + (rentepercentage/100))^n

(^ is tot de macht in pseudocode)

Nu kan je berekenen hoeveel geld je de volgende zeven jaren verdient, het bijhorende programma ziet er zo uit:

static void Main(string[] args)
{
    double startKapitaal = 15000;
    double rentepercentage = 3.5;
    double eindkapitaal;

    //Berekening eindkapitaal
    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 1);
    Console.WriteLine("Na 1 jaar:" + (int)eindkapitaal + "euro");

    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 2);
    Console.WriteLine("Na 2 jaar:" + (int)eindkapitaal + "euro");

    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 3);
    Console.WriteLine("Na 3 jaar:" + (int)eindkapitaal + "euro");

    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 4);
    Console.WriteLine("Na 4 jaar:" + (int)eindkapitaal + "euro");

    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 5);
    Console.WriteLine("Na 5 jaar:" + (int)eindkapitaal + "euro");

    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 6);
    Console.WriteLine("Na 6 jaar:" + (int)eindkapitaal + "euro");

    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), 7);
    Console.WriteLine("Na 7 jaar:" + (int)eindkapitaal + "euro");
}

Dit geeft als uitvoer:

Na 1 jaar:15524euro
Na 2 jaar:16068euro
Na 3 jaar:16630euro
Na 4 jaar:17212euro
Na 5 jaar:17815euro
Na 6 jaar:18438euro
Na 7 jaar:19084euro

Het programma werkt naar behoren, maar zoals je zelf kan zien wordt er aardig wat code herhaalt, op enkele kleine details na. Bij iedere berekening en het tonen van de interest verandert enkel de macht en het aantal jaar. Als er nu een fout in je interestberekening zou staan dan zal je die op 7 plaatsen telkens moeten veranderen.

Rente berekenen verbeterd, versie 1

We kunnen nu terug naar onze rente-berekenaar en dit programma aanzienlijk vereenvoudigen door gebruik te maken van methoden. Namelijk als volgt:

public static void RenteOpRenteBerekenen(double looptijd)
{
    double startKapitaal = 15000;
    double rentepercentage = 3.5;
    double eindkapitaal;

    //Berekening eindkapitaal
    eindkapitaal = startKapitaal * Math.Pow((1 + rentepercentage / 100), looptijd);
    Console.WriteLine("Na "+ (int)looptijd+"jaar:" + (int)eindkapitaal + "euro");
}

static void Main(string[] args)
{
    RenteOpRenteBerekenen(1);
    RenteOpRenteBerekenen(2);
    RenteOpRenteBerekenen(3);
    RenteOpRenteBerekenen(4);
    RenteOpRenteBerekenen(5);
    RenteOpRenteBerekenen(6);
    RenteOpRenteBerekenen(7);

}

Dit programma zal de zelfde output geven als het originele programma, maar de code is aanzienlijk verkleint en minder foutgevoelig (je moet maar op één plek je interestberekening aanpassen indien nodig). (Merk op dat we uiteraard de main kunnen verbeteren m.b.v. een for-loop: for(int i=0;i<8;i++) {RenteOpRenteBerekenen(i);}

Rente berekenen verbeterd, versie 2

Je code opdelen in methoden is een zeer goede eerste stap naar modulair programmeren: kleine stukken code die ieder een eigen verantwoordelijkheid hebben. Om perfect modulair te zijn moet een methode zo praktisch en algemeen mogelijk blijven, zodat de methode herbruikbaar is in andere projecten.

In het vorige voorbeeld is de methode van de renteberekening niet perfect modulair. Stel dat je later in het programma opnieuw de rente wil berekening maar niet het resultaat op het scherm wil tonen. Of stel dat je de rente wil berekenen met een andere percentage, dan kunnen we de eerder geschreven methode dus niet gebruiken.

Modulair programmeren: indien je modulair wenst te programmeren moet je je aan volgende zaken houden:

  • Beperk de methode strikt tot het uitvoeren van de opgedragen taak. Dus in het voorbeeld: alleen de renteberekening en geen verdere verwerking van de resultaten.

  • Als de methode een waarde teruggeeft, declareer hiervoor dan een passende returnwaarde.

Geef alle grootheden waarmee je de werkwijze van de methode wilt aanpassen mee aan de methode als parameter. In dit voorbeeld zijn dat dus de variabelen startkapitaal, rentepercentage en looptijd.

De nieuwe algemene, verbeterde methode wordt dan:

public static double RenteOpRenteBerekenen(double startkapitaal, double rentepercentage, double looptijd)
{
    double eindkapitaal;

    //berekenig eindkapitaal
    eindkapitaal = startkapitaal * Math.Pow((1 + rentepercentage / 100), looptijd);

    return eindkapitaal;
}

De aanroep van deze methode in de main wordt dan de volgende:

double eindkapitaal;
eindkapitaal = RenteOpRenteBerekenen(15000, 3.5, 7);
Console.WriteLine("Het eindbedrag na 7 jaar:" +(int)eindkapitaal);

Een Nuttige methode

Vaak moet je code schrijven van volgende vorm:

Console.WriteLine("Geef leeftijd");
int leeftijd= Convert.ToInt32(Console.ReadLine());

Waarbij je eerst een zinnetje toont aan de gebruiker en dan z'n input omzet naar een werkbaar getal.

Als deze constructie op meerdere plekken in een project voorkomt dan is het nuttig om deze twee lijnen naar een methode te verhuizen die er dan zo kan uitzien:

static VraagInt(string zin)
{
    Console.WriteLine(zin);
    return  Convert.ToInt32(Console.ReadLine());
}

De code van zonet kan je dan nu herschrijven naar:

int leeftijd= VraagInt("Geef leeftijd");

N-dimensionale arrays

Meer-dimensionale Arrays

Voorlopig hebben we enkel met 1-dimensionale array gewerkt. Je kan er echter ook meerdimensionale maken. Denk maar aan een n-bij-m array om een matrix voor te stellen.

Door een komma tussen rechte haakjes te plaatsen tijdens de declaratie kunnen we meer-dimensionale arrays maken.

Bijvoorbeeld 2D:

3D:

(enz.)

Om een array ook onmiddellijk te initialiseren gebruiken we dan volgende uitdrukking:

Merk op dat we dus nu een 3 bij 3 array maken. Iedere rij bestaat uit 3 elementen.

OF bij een 3D:

Stel dat we uit de books-array bijvoorbeeld de auteur van het derde boek wensen te tonen dan kunnen we schrijven:

Dit zal Mike Pastore op het scherm zetten.

En bij de temperaturen:

Zal 27 terug geven: we vragen van de laatste array ([2]), daarbinnenin de eerste array ([0]) en daarvan het tweede ([1]) element.

Lengte van iedere dimensie in een n-dimensionale matrix

Indien je de lengte opvraagt van een meer-dimensionale array dan krijg je de som van iedere lengte van iedere dimensie. Onze books array zal bijvoorbeeld dus lengte 9 hebben. Je kan echter de lengte van iedere aparte dimensie te weten komen met de GetLength() methode die iedere array heeft. Als parameter geef je de dimensie mee waarvan je de lengte wenst.

Het aantal dimensies van een array wordt trouwens weergegeven door de rank eigenschap die ook iedere array heeft. Bijvoorbeeld:

Geavanceerde methoden

Named parameters

Wanneer je een methode aanroept is de volgorde van je argumenten belangrijk: deze moeten meegeven worden in de volgorde zoals de methode parameters ze verwachten.

Met behulp van named parameters kan je echter expliciet aangeven welke argument aan welke methode-parameter moet meegegeven worden.

Stel dat we een methode hebben met volgende signatuur:

Zonder named parameters zou een aanroep van deze methode als volgt kunnen zijn:

We kunnen named parameters aangeven door de naam van de parameter gevolg door een dubbel punt en de waarde. Als we dus bovenstaande methode willen aanroepen kan dat ook als volgt met named parameters:

of ook:

Volgorde van named parameters belangrijk

Je mag ook een combinatie doen van named en gewone parameters, maar dan is de volgorde belangrijk: je moet je dan houden aan de volgorde van de methode-volgorde. Je verbeterd hiermee de leesbaarheid van je code dus (maar krijgt niet het voordeel van een eigen volgorde te hanteren). Enkele voorbeelden:

Enkele NIET GELDIGE voorbeelden:

Optionele parameters

Soms wil je dat een methode een standaard waarde voor een parameter gebruikt indien de programmeur in z'n aanroep geen waarde meegaf. Dat kan met behulp van optionele of default parameters.

Je geef aan dat een parameter optioneel is door deze een default waarde te geven in de methode-signatuur. Deze waarde zal dan gebruikt worden indien de parameter geen waarde van de aanroeper heeft gekregen.

Optionele parameters worden steeds achteraan de parameterlijst van de methode geplaatst .

In het volgende voorbeeld maken we een nieuwe methode aan en geven aan dat de laatste twee parameters (optionalstr en age) optioneel zijn:

Volgende manieren zijn nu geldige manieren om de methode aan te roepen:

Je mag enkel de optionele parameters van achter naar voor weglaten. Volgende aanroepen zijn dus niet geldig:

Met optionele parameters kunnen we dit indien gewenst omzeilen. Volgende aanroep is wel geldig:

Method overloading

Method overloading wil zeggen dat je een methode met dezelfde naam en returntype meerdere keren definieert maar met andere parameters qua type en aantal. De compiler zal dan zelf bepalen welke methode moet aangeroepen worden gebaseerd op het aantal en type parameters dat je meegeeft.

Volgende methoden zijn overloaded:

Afhankelijk van de aanroep zal dus de ene of andere uitgevoerd worden. Volgende code zal dus werken:

Betterness rule

Indien de compiler twijfelt tijdens de overload resolution (welke versie moet aangeroepen worden) zal de betterness rule worden gehanteerd: de best 'passende' methode zal aangeroepen worden. Stel dat we volgende overloaded methoden hebben:

Volgende aanroepen zullen dus als volgt uitgevoerd worden:

De betterness rule is als volgt:

Als je dus bijvoorbeeld een parameter van het type int meegeeft bij een methode aanroep (eerste kolom), dan zal een long geprefereerd worden boven een float, enz.

Indien de betterness regel niet werkt, dan zal de eerste parameter bepalen wat er gebruikt wordt. Dat zien we in volgende voorbeeld:

Indien ook die regel niet werkt dan zal een error optreden zoals hier wat zal resulteren in een Ambigious overload error:

Oefeningen deel 2

Methoden met arrays als parameter

Parkeergarage

Een parkeergarage vraagt sowieso €2.00 om tot maximum 3uur te parkeren. Per extra uur NA die 3uur wordt telkens €0.50 aangerekend (dus 4uur parkeren kost €2.50. Er wordt maximum €10 aangerekend per dag. Veronderstel dat er nooit langer dan 1 dag (24u) kan geparkeerd worden.

Schrijf een programma dat het verschuldigde bedrag toont gegeven de duur dat de parkeergarage gebruikt werd. Bij het opstarten van het programma wordt eerst gevraagd hoeveel auto’s ingevoerd zullen worden, dan wordt per auto de duur van het parkeren gevraagd. Finaal wordt, netjes getabuleerd, alle informatie getoond, inclusief het totaal bedrag. Gebruik minstens 1 methode ‘berekenKosten’ die de kost voor 1 gebruiker telkens teruggeeft, gegeven de duur als parameter. Gebruik ook een methode die een array als parameter aanvaard (bijvoorbeeld de array met daarin de respectievelijke uren per auto).

Voorbeeldoutput: Opstart:

Resultaat:

Array Viewer

Maak een programma dat een methode VisualiseerArray implementeert. De methode zal een array (type int) als parameter hebben en niets teruggeven (void). Echter, de methode zal met behulp van Write() de array, van eender welke grootte, op het scherm tonen. Tussen ieder element van dezelfde rij dient een tab (‘\t’) gezet te worden. Je dient in de methode gebruik te maken van een for-loop. Voorbeeld van main:

Geeft volgende output:

Caesar-encryptie

Maak 2 methoden Encrypt en Decrypt die als parameters telkens een chararray krijgen en een integer. Bedoeling is dat de Encrypt-methode de array van het type string versleuteld gegeven de sleutel x volgens het Caesar cipher (zie hieronder). Als resultaat komt er uit de methode de geëncrypteerde array.

De decrypt-methode doet hetzelfde maar omgekeerd: je kan er een versleutelde tekst insteken en de sleutel en de ontcijferde tekst komt terug (merk op dat je decrypt-methode gebruik kan maken van de encrypt-methode!).

Toon in je main aan dat je methoden werken (door bijvoorbeeld aan de gebruiker een stuk tekst als string te vragen en een sleutel en deze dan te encrypteren/de-crypteren).

Encryptie is de kunst van het vercijferen van data. Hierbij gaat men een gewone tekst zodanig omvormen (versleutelen) zodat deze onleesbaar is en enkel kan ontcijferd worden door de ontvanger die weet hoe de tekst terug kan verkregen worden en enkel indien deze ook de ‘private’ sleutel heeft.

Een klassiek encryptie-algoritme uit de oudheid is de Caesar substitutie. Hierbij gaan we het alfabet met x plaatsen opschuiven en vervolgens de te vercijferen tekst letter per letter vervangen met z’n respectievelijke opgeschoven versie. Hierbij is x dus de geheime sleutel die zender en ontvanger moeten afspreken.

Stel bijvoorbeeld dat x=3 dan krijgen we volgende nieuwe alfabet:

Waarbij dus de A zal vervangen worden door een D, de Z door een C, etc.

Willen we deze tekst dus encrypteren:

dan krijgen we:

Ondernemingsnummer

Ontwerp een methode waarmee je een Belgisch ondernemingsnummer kan controleren. Een Belgisch ondernemingsnummer is als volgt opgebouwd: BE 0xxx.xxx.xxx waarbij BE de landcode is die standaard gevolgd wordt door een spatie en dan een 0. Wanneer je de volgende 7 cijfers deelt door 97 in een gehele deling, dan is het getal gevormd door de laatste 2 cijfers gelijk aan 97 minus de rest van deze gehele deling. Bij een geldig ondernemingsnummer verschijnt de tekst "Geldig ondernemingsnummer.", bij een ongeldig ondernemingsnummer "Ongeldig ondernemingsnummer.". De methode aanvaardt een string waarin je het ondernemingsnummer staat.

Meer-dimensionale arrays

Determinant

Schrijf een programma dat een methode BerekenDeterminant heeft. Deze methode heeft één paramater als input: een 2 bij 2 array van integers. Als resultaat geeft de methode de determinant als integer terug. Zoek zelf op hoe je de determinant van een matrix kunt berekenen.

Volgende voorbeeld-main dient te werken,

geeft als output:

Extra: Breid uit zodat de BerekenDeterminant-methode ook werkt voor 3-bij-3 matrices. De methodeaanroep blijft dezelfde, enkel de interne code van de methode zal nu rekening moeten houden met de grootte van de matrix .

2D Array Viewer

Breid het ArrayViewer programma uit zodat ook 2-dimensionale arrays gevisualiseerd kunnen worden. (Hint: gebruik de GetLength() methode van een array).

Voorbeeld van main:

Output:

MatrixMultiplier

Schrijf een methode VermenigvuldigMatrix die 2 matrices als invoer verwacht en als resultaat een nieuwe matrix teruggeeft die het product van beide matrices bevat.

Voetbalcoach

Maak een console-applicatie voor een assistent voetbaltrainer (of een sport naar keuze).

De voetbalcoach wil na de match iedere knappe en domme actie van een speler weten. Op die manier weet hij aan het einde van de match wie er de meeste goede en slechte acties doet. De spelers hebben rugnummers 1 tot en met 12. (het gaat om een voetbal variant waar m'n geen elftal maar een twaalftal gebruikt :p)

Wanneer de coach een rugnummer intypt kan hij vervolgens ingeven of hij (a) een knappe actie of (b) een domme actie wil ingeven. Vervolgens geeft hij een getal in. Gebruik een 2dimensionale array die per speler het aantal domme en goede acties bijhoudt (de array is dus 12 bij 2 groot: 1 lijn per speler, bestaande uit 2 kolommen voor goede en domme actie. De index van de lijn is de rugnummer van speler -1).

Een typische invoer kan dus zijn:

De coach kiest dus de speler met rugnummer 2, hij kiest voor een knappe actie, en voert 6 in als aantal goede acties.

In de array op index 1 (rugnummer -1) zal in de 0'de kolom (0 = goede, 1 = slechte) het getal 6 geplaatst worden.

Vervolgens kan de coach een ander rugnummer (of hetzelfde) invoeren en zo verder.

Wanneer de coach 99 invoert stopt het programma en worden de finale statistieken getoond: per speler/rugnummer wordt het aantal goede en domme acties getoond, met daarnaast het verschil tussen beide:

(gebruik \t om goede tabs te zetten tussen de data)

Het programma toont ook welke speler(s) het meest performant waren, namelijk zij met het grootste positieve verschil tussen goede en domme acties, alsook de minst performante en de meeste “gemiddelde” speler (i.e. verschil == 0 )

Pro

Robot Simulator

" Write a robot simulator.

A robot factories' test facility needs a program to verify robot movements. The robots have three possible movements:

  • turn right

  • turn left

  • advance

    Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x, y} coordinates, e.g., {3, 8}, with coordinates increasing to the north and east.

The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.

The letter-string "RAALAL" means:

  • Turn right

  • Advance twice

  • Turn left

  • Advance once

  • Turn left yet again

Say a robot starts at {7, 3} facing north. Then running this stream of instructions should leave it at {9, 4} facing west."

Teken het gevolgde pad in de console (gebruik Console.SetCursorPosition()).

Galgje

Voorbeeld output:

Grote Som

Maak een methode Som() die eender welke hoeveelheid parameters van het type int aanvaardt en vervolgens de som van al deze parameters teruggeeft (als int).

Toon in je main aan dat de methode werkt door onder andere 1, 3, 5 en 10 als gehele getalen mee te geven.

Toon ook aan dat je een array van 50 ints als parameter kan meegeven aan de methode. (hint: je moet het params keyword gebruiken)

Deze afbeelding komt uit het zeer aan te raden handboek "Microsoft Visual C# .NET" van Joyce Farrell.

Een nadeel van arrays is dat, eens we de lengte van een array hebben ingesteld, deze lengte niet meer kan veranderen. Later zullen we leren werken met lists en andere collections die dit nadeel niet meer hebben (zie ).

Opgelet: wanneer je met arrays van objecten (zie ) werkt dan zal bovenstaande mogelijk niet het gewenste resultaten geven daar we nu de individuele referenties van een object kopieren!

Bekijk terug jouw (of mijn) oplossing van de vaardigheidsproef of de in deze cursus.

Bekijk het all-one-project : kan jij dit project afwerken zoals onderaan de opgave wordt voorgesteld?

Hoe comment getoond wordt

Volgende sectie is grotendeels gebaseerd op het volgende .

Volgende opgave komt uit

Maak een spel, vergelijkbaar met galgje, waarin de speler een woord moet raden. Zie voor de spelregels indien je deze niet kent.

hier
Zie volgend filmpje op 31 minuten.
later
deel 1 eindetest
"De verhaal generator"
Introductie methoden
Sneller methoden schrijven m.b.v. IntelliSense
string[,] books;
short[,,] temperatures;
string[,] books = {
        {"Macbeth", "Shakespeare", "ID12341"},
        {"Before I Get Old", "Dave Marsh", "ID234234"},
        {"Security+", "Mike Pastore", "ID3422134"}
    };
int[,,] temperatures= {
    {
        {3,4}, {5,4}
    },
    {
        {12,34}, {35,24}
    },
    {
        {-12,27}, {3,24}
    },
};
Console.WriteLine(books[2, 1]);
Console.WriteLine(temperatures[2, 0, 1]);
int arrayRijen = books.GetLength(0);
int arrayKolommen = books.GetLength(1);
int arrayDimensions = myColors.Rank;
 static void PrintOrderDetails(string sellerName, int orderNum, string productName)
 {
     //do stuff
 }
PrintOrderDetails("Gift Shop", 31, "Red Mug");
 PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");
 PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
PrintOrderDetails("Gift Shop", 31, productName: "Red Mug");
PrintOrderDetails(sellerName: "Gift Shop", 31, productName: "Red Mug");    // C# 7.2 onwards
PrintOrderDetails("Gift Shop", orderNum: 31, "Red Mug");
PrintOrderDetails(productName: "Red Mug", 31, "Gift Shop");
PrintOrderDetails(31, sellerName: "Gift Shop", "Red Mug");
PrintOrderDetails(31, "Red Mug", sellerName: "Gift Shop");
static void ExampleMethod(int required, string optionalstr = "default string", int age = 10)
ExampleMethod(15, "tim", 25); //klassieke aanroep
ExampleMethod(20, "dams"); //age zal 10 zijn
ExampleMethod(35); //optionalstr zal "default string" en age zal 10 zijn
ExampleMethode(3, 4); //daar de tweede param een string moet zijn
ExampleMethode(3, ,4);
ExampleMethod(3, age: 4);
static int ComputeArea(int lengte, int breedte)
{
    int opp = lengte*breedte;
    return opp;
}

static int ComputeArea(int radius)
{
    int opp = (int)(Math.PI*radius*radius);
    return opp;
}
            Console.WriteLine($"Rechthoek: {ComputeArea(5, 6)}");
            Console.WriteLine($"Circle: {ComputeArea(7)}");
static int ComputeArea(int radius) //versie A
{
    int opp = (int)(Math.PI*radius*radius);
    return opp;
}

static int ComputeArea(double radius) //versie B
{
    int opp = (int)(Math.PI * radius * radius);
    return opp;
}
Console.WriteLine($"Circle 1: {ComputeArea(7)}"); //versie A
Console.WriteLine($"Circle 2: {ComputeArea(7.5)}"); //versie B
Console.WriteLine($"Circle 3: {ComputeArea(7.3f)}"); //versie B

Meegegeven type

Voorkeur (betterness) van meeste voorkeur naar minste

byte

short, ushort, int, uint, long, ulong, float, double, decimal

sbyte

short, int long, float, double, decimal

short

int, long, float, double, decimal

ushort

int, uint, long, ulong, float, double, decimal

int

long, float, double, decimal

uint

long, ulong, float, double, decimal

long

float, double, decimal

ulong

float, double, decimal

float

double

char

ushort, int, uint, long, ulong, float, double, decimal

static void Main(string[] args)
{
    Toonverhouding(5, 3.4); //versie A
    Toonverhouding(6.2, 3); //versie B
}


static void Toonverhouding(int a, double b) //versie A
{
    Console.WriteLine($"{a}/{b}");
}


static void Toonverhouding(double a, int b) //versie B
{
    Console.WriteLine($"{a}/{b}");
}
static void Main(string[] args)
{

    Toonverhouding(5.6, 3.4);  
}


static void Toonverhouding(int a, double b)
{
    Console.WriteLine("{0}/{1}", a, b);
}


static void Toonverhouding(double a, int b)
{
    Console.WriteLine("{0}/{1}", a, b);
}
int[] array={15, 6, 9};
int[] array2={0, 1, 2, 3, 4, 5, 6};
VisualiseerArray(array);
VisualiseerArray(array2);
DEFGHIJKLMNOPQRSTUVWXYZABC
the quick brown fox jumps over the lazy dog
WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ
int[,] aMatrix = { 
                  {2, 4},
                  {3, 5}
                 };
Console.WriteLine($"Determinant van matrix is {BerekenDeterminant(aMatrix)}");
Determinant van matrix is -2
int [,] array = { {15, 6, 9}, {1, 2, 3}, {6, 9, 12}};
VisualiseerArray(array);
15  6  9
1   2  3
6   9  12
2

a

6
Rugnummer   Goede   Domme   Verschil
1               5       2        3
2               6       7       -1
artikel
Originele bron voor dit idee
Exercism.io
Wiki

OOP Intro

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 Smalltalk die het eerst de term Object Oriented Programming introduceerde.

In recentere jaren heeft deze techniek echter ook zijn weg gevonden naar webscripting 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 meestal een voorwerp uit het dagelijks leven als voorbeeld gebruikt. Neem bijvoorbeeld een auto. De auto is het object en dit object heeft bepaalde properties of eigenschappen. Een eigenschap van de auto kan een band, een deur, een spiegel, de uitlaat of eender welk ander onderdeel van de auto zijn. Maar een auto heeft ook methoden. Een methode kan starten of remmen zijn.

Black-box principe

Een belangrijk concept bij OOP is het Black-box principe waarbij we de afzonderlijke objecten en hun werking als kleine zwarte dozen gaan beschouwen. 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 interface noemen, die je kan 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. Vergelijk het met de methoden uit vorig semester: "if it works, it works" en dan hoef je niet in de code van de methode te gaan zien wat er juist gebeurt.

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 auto's. De verschillende auto's die je op de straat ziet rijden zijn allemaal objecten van die klasse. De klasse zelf is een soort algemene beschrijving waaraan alle objecten van dat type moeten voldoen (bv: alle auto's hebben 4 banden, 1 motor en kunnen sneller en trager rijden).

Definitie klasse en object

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

  • Een individueel object is een instantie van een klasse

Je zou dit kunnen vergelijken met het grondplan voor een huis dat tien keer in een straat zal gebouwd worden. Het plan met alle soortgelijke eigenschappen van ieder huis is de klasse. De effectieve huizen die we, gebaseerd op dat grondplan, bouwen zijn de instanties of objecten van deze klasse.

Nog een andere invalshoek:

Een andere invalshoek is de zogenaamde "dungeons" in veel online games. De makers van het spel hebben iedere dungeon in een klasse beschreven. Wanneer een groep avonturiers nu in zo'n grot gaat dan wordt voor die groep een aparte instantie (instance) van die grot gemaakt, gebasseerd op de klasse. Ze doen dit zodat iedere groep spelers mekaar niet voor de voeten loopt in 1 grot.

Objecten in de woorden van Steve Jobs

Jeff Goodell: Would you explain, in simple terms, exactly what object-oriented software is?

Steve Jobs: Objects are like people. They’re living, breathing things that have knowledge inside them about how to do things and have memory inside them so they can remember things. And rather than interacting with them at a very low level, you interact with them at a very high level of abstraction, like we’re doing right here.

Here’s an example: If I’m your laundry object, you can give me your dirty clothes and send me a message that says, “Can you get my clothes laundered, please.” I happen to know where the best laundry place in San Francisco is. And I speak English, and I have dollars in my pockets. So I go out and hail a taxicab and tell the driver to take me to this place in San Francisco. I go get your clothes laundered, I jump back in the cab, I get back here. I give you your clean clothes and say, “Here are your clean clothes.”

You have no idea how I did that. You have no knowledge of the laundry place. Maybe you speak French, and you can’t even hail a taxi. You can’t pay for one, you don’t have dollars in your pocket. Yet, I knew how to do all of that. And you didn’t have to know any of it. All that complexity was hidden inside of me, and we were able to interact at a very high level of abstraction. That’s what objects are. They encapsulate complexity, and the interfaces to that complexity are high level.

En omdat het vloeken in de kerk is om Steve Jobs in een C# cursus aan het woord te laten, hier wat Microsoft-oprichter Bill Gates over OOP te zeggen had:

Werken met arrays

Nuttige array methoden

Net zoals we hebben gezien dat de Math-klasse een heleboel nuttige methoden in zich heeft, zo heeft ook iedere array een aantal methoden waar handig gebruik van gemaakt kan worden.

Om deze methoden te kunnen gebruiken moet je bovenaan je file de volgende lijn toevoegen: using System.Linq;:

using System;
namespace methodmovie
{
   class Program
    {
        public static void Main()
        {
            int[] getallen = new int[101];

            //..
            Console.WriteLine(getallen.Sum());
        }
    }
}

Wanneer je een array hebt gemaakt kan je met de IntelliSense van Visual Studio bekijken wat je allemaal kan doen met de array:

Al deze methoden hier beschrijven zal ons te ver nemen. De volgende methoden zijn echter zeer handig om te gebruiken:

Max(), Min(), Sum() en Average().

Volgende code geeft bijvoorbeeld het grootste getal terug uit een array genaamd "leeftijden":

int oudsteleeftijd = leeftijden.Max();

System.Array

Alle C# arrays erven over van de System.Array klasse (klasse en overerving zien we later), hierdoor kan je zaken zoals Length gebruiken op je array. De System.Array klasse heeft echter ook nog een hoop andere nuttige methoden zoals de BinarySearch(), Sort() en Reverse() methoden. Het gebruik hiervan is steeds dezelfde zoals volgende voorbeelden tonen:

Sort: Arrays sorteren

Om arrays te sorteren roep je de Sort()-methode op als volgt, als parameter geef je de array mee die gesorteerd moet worden.

Volgende voorbeeld toont hier het gebruik van:

string[] myColors = {"red", "green", "yellow", "orange", "blue"};
//Sorteer
Array.Sort(myColors);

//Toon resultaat van sorteren
for (int i = 0; i < myColors.Length; i++)
{
    Console.WriteLine(myColors[i]);
}

Wanneer je de Sort-methode toepast op een array van strings dan zullen de elementen alfabetisch gerangschikt worden.

Reverse: Arrays omkeren

Met de Array.Reverse()-methode kunnen we dan weer de elementen van de array omkeren (dus het laatste element vooraan zetten en zo verder:

Array.Reverse(myColors);

Clear: Arrays leegmaken

Een array volledig leegmaken (alle elementen op ‘null’ zetten) doe je met de Array.Clear-methode, als volgt:

Array.Clear(myColors);

BinarySeach: Zoeken in arrays

De BinarySearch-methode maakt het mogelijk om te zoeken naar de index van een gegeven element in een index. Deze methode werkt enkel indien de elementen in de array gesorteerd staan! Je geeft aan de methode 2 parameters mee, enerzijds de array in kwestie en anderzijds het element dat we zoeken. Als resultaat wordt de index van het gevonden element teruggegeven. Indien niets wordt gevonden zal het resultaat -1 zijn.

Volgende code zal bijvoorbeeld de index teruggeven van de kleur "red" indien deze in de array myColors staat:

Array.BinarySearch(myColors, "red");

Volgend voorbeeld toont het gebruik van deze methode:

int[] rank = {224, 34, 156, 1023, -6};
Array.Sort(rank);

Console.WriteLine("What rank do you need?");
int userchoice = Convert.ToInt32(Console.ReadLine());

int index = Array.BinarySearch(rank, userchoice);
if(index >= 0)
    Console.WriteLine($"{userchoice} found at index {index}");
else
    Console.WriteLine("Not found");

Copy : Array kopieren

In het vorige hoofdstuk vertelden we reeds over het venijn van arrays kopiëren, daar deze 'by reference' worden bewaard. Lijn 2 in deze code creëert dus enkel een alias naar dezelfde array en geen kopie:

int[] arrayA = {1, 2, 3};
int[] arrayB = arrayA; //Sure?!

Willen we een kopie dan moet dit dus zoals in vorige hoofdstuk manueel gebeuren, of je maakt gebruikt van de Array.Copy() methode, als volgt:

int[] ar = {1, 2, 3};
int[] bar = new int[ar.Length];
Array.Copy(ar, bar, ar.Length);

De methode Array.Copy vereist minimaal 3 parameters, waaronder de originele array, de doel array (die reeds moet aangemaakt zijn!) alsook hoeveel elementen je uit de originele array wenst te kopieren. Bekijk zeker ook de overloaded versies die deze methode heeft. Zo kan je ook een bepaald stuk van een array kopieren en ook bepalen waar in de doel array dit stuk moet komen.

Manueel zoeken in arrays

Het zoeken in arrays kan met behulp van while of for-loops tamelijk snel. Volgend programmatje gaat zoeken of het getal 12 aanwezig is in de array. Indien ja dan wordt de index bewaard van de positie in de array waar het getal staat:

int teZoekenGetal = 12;

int[] getallen = {5, 10, 12, 25, 16};

bool gevonden = false;
int index = -1;

for (int i = 0; i < getallen.Length; i++)
{
    if (getallen[i] == teZoekenGetal)
    {
        gevonden = true;
        index = i;
    }
}

Voorgaande stukje code is de meest naïeve oplossing. Bedenk echter wat er gebeurt indien het getal dat we zoeken 2 of meerdere keren in de array staat. Index zal dan de positie bevatten van de laatst gevonden 12 in de array.

Het is zéér belangrijk dat je vlot dit soort algoritmen kan schrijven, zoals:

  • Zoeken van elementpositie in array

  • Tellen hoe vaak een element in een array voorkomt

  • Elementen in een array 1 of meerdere plaatsen opschuiven

Manueel zoeken met for en while

We tonen nu twee voorbeelden van hoe je kan zoeken in een array wanneer we bijvoorbeeld 2 arrays hebben die 'synchroon' zijn. Daarmee bedoel ik: de eerste array bevat producten, de tweede array bevat de prijs van ieder product. De prijs van de producten staat steeds op dezelfde index in de andere array:

string[] products = {"apples", "pears", "melons"};
double[] prices = {3.3, 6.2, 2.9};

We vragen nu aan de gebruiker van welk product de prijs getoond moet worden:

Console.WriteLine("Which price do you need?");
string userchoice = Console.ReadLine();

We tonen nu hoe we met for eerst het juiste product zoeken en dan vervolgens die index bewaren en gebruiken om de prijs te tonen:

bool found = false;
int productIndex = -1;

int counter = 0;
while (counter < products.Length && userchoice != products[counter])
{
    counter++;
}

if (counter != products.Length) //product found!
{
    found = true;
    productIndex = counter;
}


if (found == true)
{
    Console.WriteLine($"Price for {userchoice} is {prices[productIndex]}");
}
else
{
    Console.WriteLine("Not found");
}

Een nadeel van deze oplossing is dat we steeds de hele for doorlopen (we gebruiken geen break vanwege een allergie hiervoor bij de auteur). Bij heel lange arrays is dit dus niet erg performant.

Volgende oplossing met een while toont een performantere oplossing:

bool found = false;
int productIndex = -1;

int counter = 0;
while (counter < products.Length && userchoice != products[counter])
{
    counter++;
}

if (counter != products.Length) //product found!
{
    found = true;
    productIndex = counter;
}

if (found == true)
{
    Console.WriteLine($"Price for {userchoice} is {prices[productIndex]}");
}
else
{
    Console.WriteLine("Not found");
}

String en arrays

String naar char array

Het type string is niet meer dan een arrays van karakters, char[]. Om een string per karakter te bewerken is het aanbevolen om deze naar een char-array om te zetten en nadien terug naar een string. Dit kan gebruikmakend van .ToCharArray() als volgt:

string origineleZin = "Ik ben Tom";
char[] karakters = origineleZin.ToCharArray();
karakters[8] = 'i';
string nieuweZin = new string(karakters);
Console.WriteLine(nieuweZin);

De uitvoer zal worden:Ik ben Tim.

Char array naar string

Ook de omgekeerde weg is mogelijk. De werking is iets anders, let vooral op hoe we de char array doorgeven als argument bij het aanmaken van een nieuwe string in lijn 3:

char[] arrayOfLetters = {'h', 'a', 'l', 'l', 'o'};
arrayOfLetters[2] = 'x';
string word = new string(arrayOfLetters);
Console.WriteLine(word);

De uitvoer van deze code zal zijn: haxlo.

Klassen en objecten in C#

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

Klasse maken

Een klasse heeft de volgende vorm:

[optionele access modifier] class className
{

}

Volgende code beschrijft de klasse auto in C#

class Auto
{

}

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

De optionele access modifier komen we later op terug.

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:

  • In de solution explorer, rechterklik op je project

  • Kies Add

  • Kies Class..

  • Geef een goede naam voor je klasse

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

Objecten aanmaken

Je kan nu objecten aanmaken van de klasse die je hebt gedefinieerd. Je doet dit door eerst een variabele te definiëren en vervolgens een object te instantiëren met behulp van het new keyword:

Auto mijnEerste = new Auto();
Auto mijnAndereAuto = new Auto();

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

Je hebt dus in het verleden ook al objecten aangemaakt. 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.

Oefeningen deel 1

Opwarmers

Een kort programma om op te warmen. Iedere opdracht hieronder is een apart programma (uiteraard mag je dit ook in 1 project oplossen).

Gebruik steeds een loop om dit op te lossen.

  • Maak een array gevuld met de getallen 0 tot 10

  • Maak een array gevuld met de getallen van 100 tot 1

  • Maak een array gevuld met de letters a tot z

  • Maak een array gevuld met willekeurige getallen tussen 1 en 100 (de array is 20 lang)

  • Maak een array gevuld met afwisselend true en false (de array is 30 lang)

Per array: output de array naar het scherm, maar ieder element naast elkaar met komma gescheiden. Dus niet:

true
false
true
\\etc

maar wel: true, false, true, ...

ArrayOefener 1

Maak een programma dat aan de gebruiker vraagt om 10 waarden (int) in te voeren in een array. Vervolgens toont het programma de som, het gemiddelde en het grootste getal van deze 10.

Vervolgens vraagt het programma de gebruiker om een getal in te voeren. Het programma toont dan alle getallen die groter of gelijk zijn aan dit ingevoerde getal zijn die in de array aanwezig zijn. Indien geen getallen groter zijn dan verschijnt een bericht Niets is groter op het scherm.

ArrayOefener 2

Maak een programma dat aan de gebruiker vraagt om 2 keer 5 getallen in te voeren. Bewaar de eerste reeks waarden in een array A, de tweede reeks waarden in array B. Maak een nieuwe array C aan die steeds de som bevat van het respectievelijke element uit arrays A en B. Toon het resultaat.

Vraag Array

Maak een array die 6 strings kan bevatten. Ieder element van de array bevat een vraag (naar keuze te verzinnen) als string waar de gebruiker met een getal op moet antwoorden. Maak een array aan die tot 6 ints kan bevatten. Lees 1 voor 1 de vraag uit de string-array uit en toon deze op het scherm. Lees vervolgens het antwoord uit dat de gebruiker intypt en bewaar dit als int in de 2e array.

Na de 6 vragen toon je vervolgens de vragen opnieuw met achter iedere vraag het antwoord van de gebruiker.

Array Zoeker

Maak een programma dat eerst weer aan de gebruiker om 10 waarden vraagt die in een array worden gezet.

Vervolgens vraagt het programma welke waarde verwijderd moet worden. Wanneer de gebruiker hierop antwoordt met een nieuwe waarde dan zal deze nieuw ingevoerde waarde in de array gezocht worden. Indien deze gevonden wordt dan wordt deze waarde uit de array verwijderd en worden alle waarden die erachter komen met een plaatsje naar links opgeschoven, zodat achteraan de array terug een lege plek komt.

Deze laatste plek krijgt de waarde -1.

Toon vervolgens alle waarden van de array.

Indien de te zoeken waarde meer dan 1 keer voorkomt, wordt enkel de eerst gevonden waarde verwijderd.

LeveringsBedrijf

Maak een programma voor een koeriersbedrijf. Maak een array die 10 postcodes bevat (zelf te kiezen) van gemeenten waar het bedrijf naar levert. Maak een tweede array die de prijs bevat per kg van iedere respectievelijke gemeente. Het eerste element van deze array bevat dus de prijs/kg om naar de gemeente die als eerste in de array met postcodes staat.

Vraag aan de gebruiker een postcode en het gewicht van het pakket. Vervolgens wordt de prijs opgezocht voor die gemeente en wordt deze berekend gegeven het ingegeven gewicht.

Indien het bedrijf niet levert aan de ingetypte postcode dan wordt een foutmelding weergegeven.

Geef gewicht pakket
45
Naar welke postcode wenst u dit pakket te versturen?
2020
Dit zal 9630 euro kosten.

Bob

"Bob is a lackadaisical teenager. In conversation, his responses are very limited.

Bob answers 'Sure.' if you ask him a question.

He answers 'Whoa, chill out!' if you yell at him.

He answers 'Calm down, I know what I'm doing!' if you yell a question at him.

He says 'Fine. Be that way!' if you address him without actually saying anything.

He answers 'Whatever.' to anything else."

Bekijk steeds de laatste 2 tekens die de gebruiker invoert om de response van Bob te bepalen.

Kan je een gesofisticeerdere bot maken? (tip: myInputstring.Contains(somestring) geeft een bool terug indien somestring voorkomt in de variabele myInputstring van het type string )

Gebruik een array om de antwoorden in te bewaren.

PRO: kan je met een array eerdere vragen van de gebruiker bewaren om zo complexere antwoorden te genereren?

Hamming distance

De hamming distance is het aantal tekens dat twee reeksen verschillen indien we ieder element vergelijken op dezelfde plaats in de andere reeks.

Maak een programma dat aan de gebruiker vraagt om twee DNA strings in te voeren (een reeks bestaande uit de letters G, A, C & T). Beide reeksen moeten even lang zijn.

Bereken de hamming distance tussen beide reeksen.

De hamming distance van volgende twee DNA strings is 7, omdat er 7 elementen in beide strings staan die niet gelijk zijn aan mekaar op dezelfde plek (aangeduid met ^).

GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
^ ^ ^  ^ ^    ^^

Methoden met arrays als parameter

Parkeergarage

Een parkeergarage vraagt sowieso €2.00 om tot maximum 3uur te parkeren. Per extra uur NA die 3uur wordt telkens €0.5 aangerekend (dus 4uur parkeren kost €2.5. Er wordt maximum €10 aangerekend per dag. En veronderstel dat er nooit langer dan 1 dag (24u) kan geparkeerd worden.

Schrijf een programma dat het verschuldigde bedrag toont gegeven de duur van een auto. Bij het opstarten van het programma wordt eerst gevraagd hoeveel auto’s ingevoerd zullen worden, dan wordt per auto de duur van het parkeren gevraagd. Finaal wordt, netjes getabuleerd, alle informatie getoond, inclusief het totaal bedrag. Gebruik minstens 1 methode ‘berekenKosten’ die de kost voor 1 gebruiker telkens teruggeeft, gegeven de duur als parameter. Gebruik ook een methode die een array als parameter aanvaard (bijvoorbeeld de array met daarin de respectievelijke uren per auto).

Voorbeeldoutput: Opstart:

Resultaat:

Voetbalcoach

Maak een console-applicatie voor een assistent voetbaltrainer (of een sport naar keuze)

De voetbalcoach wil na de match iedere knappe en domme actie van een speler weten. Op die manier weet hij aan het einde van de match wie er de meeste goede en slechte acties doet. De spelers hebben rugnummers 1 tot en met 12. (het gaat om een voetbal variant waar m'n geen elftal maar een twaalftal gebruikt :p)

Wanneer de coach een rugnummer intypt kan hij vervolgens ingeven of hij (a) een knappe actie of (b) een domme actie wil ingeven. Vervolgens geeft hij een getal in . Gebruik een 2dimensionale array die per speler het aantal domme en goede acties bijhoudt (de array is dus 12 bij 2 groot: 1 lijn per speler, bestaande uit 2 kolommen voor goede en domme actie. De index van de lijn is de rugnummer van speler -1 ).

Een typische invoer kan dus zijn:

2

a

6

De coach kiest dus de speler met rugnummer 2, hij kiest voor een knappe actie, en voert 6 in als aantal goede acties.

In de array op index 1 (rugnummer -1 ) zal in de de 0'de kolom(0=goede, 1=slechte) het getal 6 geplaatst worden.

Vervolgens kan de coach een ander rugnummer (of hetzelfde) invoeren en zo verder.

Wanneer de coach 99 invoert stopt het programma en worden de finale statistieken getoond: per speler/rugnummer wordt het aantal goede en domme acties getoond, met daarnaast het verschil tussen beide:

(gebruik \t om goede tabs te zetten tussen de data)

Rugnummer   Goede   Domme   Verschil
1               5       2        3
2               6       7       -1

Het programma toont ook welke speler(s) het meest performant waren, namelijk zij met het grootste positieve verschil tussen goede en domme acties, alsook de minst performante en de meeste “gemiddelde” speler (i.e. verschil == 0 )

Array Viewer

Maak een programma dat een methode VisualiseerArray implementeert. De methode zal een array (type int) als parameter hebben en niets teruggeven (void). Echter, de methode zal met behulp van Write() de array, van eender welke grootte, op het scherm tonen. Tussen ieder element van dezelfde rij dient een tab (‘\t’) gezet te worden. Je dient in de methode gebruik te maken van een for-loop. Voorbeeld van main:

int[] array={15,6,9};
int[] array2={0,1,2,3,4,5,6};
VisualiseerArray(array);
VisualiseerArray(array2);

Geeft volgende output:

Caesar-encryptie

Maak 2 methoden Encrypt en Decrypt die als parameters telkens een chararray krijgen en een integer. Bedoeling is dat de Encrypt-methode de array van het type string versleuteld gegeven de sleutel x volgens het Caesar cipher (zie hieronder). Als resultaat komt er uit de methode de geëncrypteerde array.

De decrypt-methode doet hetzelfde maar omgekeerd: je kan er een versleutelde tekst insteken en de sleutel en de ontcijferde tekst komt terug (merk op dat je decrypt-methode gebruik kan maken van de encrypt-methode!).

Toon in je main aan dat je methoden werken (door bijvoorbeeld aan de gebruiker een stuk tekst als string te vragen en een sleutel en deze dan te encrypteren/de-crypteren).

Encryptie is de kunst van het vercijferen van data. Hierbij gaat men een gewone tekst zodanig omvormen (versleutelen) zodat deze onleesbaar is en enkel kan ontcijferd worden door de ontvanger die weet hoe de tekst terug kan verkregen worden en enkel indien deze ook de ‘private’ sleutel heeft.

Een klassiek encryptie-algoritme uit de oudheid is de Caesar substitutie. Hierbij gaan we het alfabet met x plaatsen opschuiven en vervolgens de te vercijferen tekst letter per letter te vervangen met z’n respectievelijke opgeschoven versie. Hierbij is x dus de geheime sleutel die zender en ontvanger moeten afspreken.

Stel bijvoorbeeld dat x=3 dan krijgen we volgende nieuwe alfabet:

DEFGHIJKLMNOPQRSTUVWXYZABC

Waarbij dus de A zal vervangen worden door een D, de Z door een C, etc.

Willen we deze tekst dus encrypteren:

the quick brown fox jumps over the lazy dog

dan krijgen we:

WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ

Ondernemingsnummer

Ontwerp een methode waarmee je een Belgisch ondernemingsnummer kan controleren. Een Belgisch ondernemingsnummer is als volgt opgebouwd: BE 0xxx.xxx.xxx waarbij BE de landcode is die standaard gevolgd wordt door een spatie en dan een 0. Wanneer je de volgende 7 cijfers deelt door 97 in een gehele deling, dan is het getal gevormd door de laatste 2 cijfers gelijk aan 97 minus de rest van deze gehele deling. Bij een geldig ondernemingsnummer verschijnt de tekst "Geldig ondernemingsnummer.", bij een ongeldig ondernemingsnummer "Ongeldig ondernemingsnummer.". De methode aanvaardt een string waarin je de ondernemingsnummer staat.

Meer-dimensionale arrays

Determinant

Schrijf een programma dat een methode BerekenDeterminant heeft. Deze methode heeft één paramater als input: een 2 bij 2 array van integers. Als resultaat geeft de methode de determinant als integer terug. Zoek zelf op hoe je de determinant van een matrix kunt berekenen.

Volgende voorbeeld-main dient te werken,

int[,] aMatrix={ 
                  {2,4},
                  {3,5}
                };
Console.WriteLine($"Determinant van matrix is {BerekenDeterminant(aMatrix)}");

geeft als output:

Determinant van matrix is -2

Extra: Breid uit zodat de BerekenDeterminant-methode ook werkt voor 3-bij-3 matrices. De methodeaanroep blijft dezelfde, enkel de interne code van de methode zal nu rekening moeten houden met de grootte van de matrix .

2D Array Viewer

Breid het ArrayViewer programma uit zodat ook 2-dimensionale arrays gevisualiseerd kunnen worden. (Hint: gebruik de GetLength() methode van een array).

Voorbeeld van main:

int [,] array= { {15,6,9},{1,2,3},{6,9,12}};
VisualiseerArray(array);

Output:

15  6  9
1   2  3
6   9  12

MatrixMultiplier

Schrijf een methode VermenigvuldigMatrix die 2 matrices als invoer verwacht en als resultaat een nieuwe matrix teruggeeft die het product van beide matrices bevat.

Robot Simulator

" Write a robot simulator.

A robot factories' test facility needs a program to verify robot movements. The robots have three possible movements:

  • turn right

  • turn left

  • advance

    Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, e.g., {3,8}, with coordinates increasing to the north and east.

The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.

The letter-string "RAALAL" means:

  • Turn right

  • Advance twice

  • Turn left

  • Advance once

  • Turn left yet again

Say a robot starts at {7, 3} facing north. Then running this stream of instructions should leave it at {9, 4} facing west."

Teken het gevolgde pad in de console (gebruik Console.SetCursorPosition()).

Pro

Galgje

Voorbeeld output:

Grote Som

Maak een methode Som() die eender welke hoeveelheid paramaters van het type int aanvaardt en vervolgens de som van al deze parameters teruggeeft (als int).

Toon in je main aan dat de methode werkt door onder andere 1, 3, 5 en 10 als gehele getalen mee te geven.

Toon ook aan dat je een array van 50 ints als parameter kan meegeven aan de methode. (hint:je moet het params keyword gebruiken)

Properties

Properties

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

  1. Full properties

  2. Auto properties

In een wereld zonder properties

Properties (eigenschappen) zijn de C# manier om objecten hun interne staat in en uit te lezen. Ze zorgen voor een gecontroleerde toegang tot de interne structuur van je objecten.

Stel dat we volgende klasse hebben:

public class SithLord
{
    private int energy;
    private string sithName;
}

Een SithLord heeft steeds een verborgen Sith Name en ook een hoeveelheid energie die hij nodig heeft om te strijden. Het is uit den boze dat we eenvoudige data fields (energy en name) public maken. Zouden we dat wel doen dan kunnen externe objecten deze geheime informatie uitlezen!

SithLord palpatine = new SithLord();
Console.WriteLine(palpatine.sithName); //DIT ZAL DUS NIET WERKEN, daar sithName private is.

We willen echter wel van buiten uit het energy-level van een SithLord kunnen instellen. Maar ook hier hetzelfde probleem: wat als we de energy-level op -1000 instellen? Terwijl energy nooit onder 0 mag gaan.

Properties lossen dit probleem op

Oldschool oplossing

Vroeger loste men voorgaande probleem op door Get-methoden te schrijven:

Je zal deze manier nog in veel andere talen tegenkomen. Wij prefereren properties zoals we nu zullen uitleggen.

public class SithLord
{
    private int energy;
    private string sithName;

    public void SetSithName(string newname)
    {
        sithName = newname;
    }

    public string GetSithName()
    {
        return "YOU WISH!";
    }

    public void SetEnergy(int value)
    {
        if(value > 0 && value < 9999)
            energy = value;
    }

    public int GetEnergy()
    {
        return energy;
    }
}

Je zou dan kunnen doen:

SithLord vader = new SithLord();
vader.SetEnergy(20); 
Console.WriteLine($"Vaders energy is {vader.GetEnergy()}"); //get

Full properties

Een full property ziet er als volgt uit:

class SithLord
{
    private int energy;
    private string sithName;

    public int Energy
    {
        get
        {
            return energy;
        }
        set
        {
            energy = value;
        }
    }
}

Dankzij deze code kunnen we nu elders dit doen:

SithLord vader = new SithLord();
vader.Energy = 20; //set
Console.WriteLine($"Vaders energy is {Vader.Energy}"); //get

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 int Energy: een property is normaal public. Vervolgens zeggen we wat voor type de property moet zijn en geven we het een naam. Indien je de property gaat gebruiken om een intern dataveld naar buiten beschikbaar te stellen, dan is het een goede gewoonte om dezelfde naam als dat veld te nemen maar nu met een hoofdletter. (dus Energy i.p.v. energy).

  • { }: 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 energy maar dit mag even goed bijvoorbeeld return 4 of een hele reeks berekeningen zijn. Het element dat je returnt in de get code moet uiteraard van hetzelfde type zijn als waarmee je de property hebt gedefinieerd (int in dit geval).

    • We kunnen nu van buitenaf toch de waarde van energy uitlezen via de property en het get-gedeelte: Console.WriteLine(palpatine.Energy);

  • 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 interne variabele aan te passen. De waarde die we van buitenuit krijgen (als een parameter zeg maar) zal altijd in een lokale variabele value worden bewaard. Deze zal van het type van de property zijn. Vervolgens kunnen we value toewijzen aan de interne variabele indien gewenst: energy=value

    • We kunnen vanaf nu van buitenaf waarden toewijzen aan de property en zo energyvtoch bereiken: palpatine.Energy=50.

Snel property schrijven

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

Full property met toegangscontrole

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

Properties hebben echter de mogelijkheid om op te treden als wachters van en naar de interne staat van objecten.

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

   public int Energy
    {
        get
        {
            return energy;
        }
        set
        {
            if(value >= 0)
                energy = value;
        }
    }

Enkel indien de toegewezen waarde groter of gelijk is aan 0 zal deze ook effectief aan energy toegewezen worden. Volgende lijn zal dus geen effect hebben: Palpatine.Energy=-1;

We kunnen de code binnen set (en get) zo complex als we willen maken.

Property variaties

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

Write-only property

   public int Energy
    {
        set
        {
            if(value >= 0)
                energy = value;
        }
    }

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

Read-only property

   public int Energy
    {
        get
        {
            return energy;
        }
    }

We kunnen dus enkel energy van buitenuit uitlezen, maar niet aanpassen.

Opgelet: het readonly keyword heeft andere doelen en wordt NIET gebruikt in C# om een readonly property te maken

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:

   public int Energy
    {
        get
        {
            return energy;
        }
        private set
        {
            if(value >= 0)
                energy = value;
        }
    }

Van buitenuit zal enkel code werken die deget-van deze property aanroept: Console.WriteLine(palpatine.Energy);. Code die de set van buitenuit nodig heeft zal een fout geven zoals: palpatine.Energy=65; ongeacht of deze geldig is of niet.

Nu goed opletten: indien we in het object de property willen gebruiken dan moeten we deze dus ook effectief aanroepen, anders omzeilen we hem als we rechtstreeks energy instellen.

Kijk zelf naar volgende slechte code:

class SithLord
{
    private int energy;
    private string sithName;

    public void ResetLord()
    {
        energy = -1;
    }

    public int Energy
    {
        get
        {
            return energy;
        }
        private set
        {
            if(value >= 0)
                energy = value;
        }
    }
}

De nieuw toegevoegde methode ResetLord willen we gebruiken om de lord z'n energy terug te verlagen. Door echter energy=-1 te schrijven geven we dus -1 rechtstreeks aan energy. Nochtans is dit een illegale waarde. We moeten dus in de methode ook expliciet via de property gaan en dus schrijven:

public void ResetLord()
{
    Energy = -1; // Energy i.p.v. energy
}

Het is een goede gewoonte om zo vaak mogelijk via de properties je interne variabele aan te passen en niet rechtstreeks het dataveld zelf.

Read-only Get-omvormers

Je bent uiteraard niet verplicht om voor iedere interne variabele een bijhorende property te schrijven. Omgekeerd ook: mogelijk wil je extra properties hebben voor data die je 'on-the-fly' kan genereren.

Stel dat we volgende klasse hebben

public class Person
{
    private string firstName;
    private string lastName;
}

We willen echter ook soms de volledige naam op het scherm tonen ("Voornaam + Achternaam"). Via een read-only property kan dit supereenvoudig:

public class Person
{
    private string firstName;
    private string lastName;
    public string FullName
    {
        get{ return $"{firstName} {lastName}";}
    }
    public string Email
    {
        get
        {
            return $"{firstName}.{lastName}@ap.be";
        }
    }
}

Of nog eentje:

public class Earth
{
    public double GravityConstant
    {
        get
        {
            return 9.81;
        }
    }
}

Nog een voorbeeldje:

public class Person
{
    private int age;

    public bool IsProbablyAlive
    {
        get
        {
            if(age > 120) return false;
            return true;
        }
    }
}

Vaak gebruiken we dit soort read-only properties om data te transformeren. Stel bijvoorbeeld dat we volgende klasse hebben:

public class Person
{
    private int age; //in jaren

    public int AgeInMonths
    {
        get
        {
            return age * 12;
        }
    }
}

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.

Zo kan je eenvoudige de klasse Person herschrijven met behulp van autoproperties. De originele klasse:

public class Person
    {

        private string firstName;
        private string lastName;
        private int age;

        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
            }
        }

        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
            }
        }

        public int Age
        {
            get
            {
                return age;
            }
            set
            {
                age = value;
            }
        }
    }

De herschreven klasse met autoproperties (autoprops):

public class Person
    {

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }

    }

Beide klassen hebben exact dezelfde functionaliteit, echter de klasse aan de rechterzijde is aanzienlijk eenvoudig om te lezen en te typen.

Beginwaarde van autoprops

Je mag autoproperties beginwaarden geven door de waarde achter de property te geven, als volgt:

public int Age {get;set;} = 45;

Altijd auto-properties?

Merk op dat je dit enkel kan doen indien er geen extra logica in de property aanwezig is. Stel dat je bij de setter van age wil controleren op een negatieve waarde, dan zal je dit zoals voorheen moeten schrijven en kan dit niet met een automatic property:

set
{
    if( value > 0)
        age = value;
}

Voorgaande property kan dus NIET herschreven worden met een automatic property.

Alleen-lezen eigenschap

Je kan automatic properties ook gebruiken om bijvoorbeeld een read-only property te definiëren . Als volgt:

Originele klasse:

public string FirstName
{

    get
    {
        return firstName;
    }
}

Met autoprops:

public string FirstName { get; private set;}

En andere manier die ook kan is als volgt:

public string FirstName { get; }

Snel autoproperties typen in Visual Studio:

Als je in Visual Studio in je code prop typt en vervolgens twee keer de tabtoets indrukt dan verschijnt al de nodige code voor een automatic property. Je hoeft dan enkel nog volgende zaken in orde te brengen:

  • Het type van de property

  • De naam van de property (identifier)

  • De toegankelijkheid van get/set (public, private, protected)

Via propg krijg je aan autoproperty met private setter.

Methode of property

Een veel gestelde vraag bij beginnende OO-ontwikkelaars is: "Moet dit in een property of in een methode gestoken worden?"

De regel is eenvoudig:

  • Betreft het een actie, iets dat het object moet doen (tekst tonen, iets berekenen, etc) dan plaats je het in een methode

  • Betreft het een eigenschap die een bepaalde waarde heeft, dan gebruik je een property

Kennisclip

Arrays en methoden

Methoden met arrays als parameter maken

Zoals alle types kan je ook arrays van eender welk type als parameter gebruiken bij het schrijven van een methode.

Opgelet:

Arrays worden altijd ‘by reference’ doorgegeven aan een methode. Dit heeft twee belangrijke gevolgen:

  1. Je hoeft het ref keyword niet mee te geven, dit gebeurt impliciet reeds

  2. Je werkt steeds met de eigenlijke array, ook in de methode. Als je dus aanpassingen aan de array aanbrengt in de methode, dan zal dit ook gevolgen hebben op de array van de parent-methode (logisch: het gaat om dezelfde array).

Stel dat je bijvoorbeeld een methode hebt die als parameter 1 array van ints meekrijgt. De methode zou er dan als volgt uitzien.

static void EenVoorbeeldMethode(int[] inArray)
{

}

Array grootte in de methode

Een array als parameter meegeven kan dus, maar een ander aspect waar rekening mee gehouden moet worden is dat je niet kan ingeven in de parameterlijst hoe groot de array is! Je zal dus in je methode steeds de grootte van de array moeten uitlezen met de Length-eigenschap.

Volgende methode is dus FOUT!

static void EenVoorbeeldMethode(ref int[6] inArray)
{

}

En zal volgende error genereren:

Arraymethode voorbeeld

Volgend voorbeeld toont een methode die alle getallen van de array op het scherm zal tonen:

static void ToonArray(int[] getalArray)
{
    Console.WriteLine("Array output:");
    for (int i = 0; i < getalArray.Length; i++)
    {
        Console.WriteLine(getalArray[i]);
    }
}

Stel dat je elders volgende array hebt int[] leeftijden = {2, 5, 1, 6};. De ToonArray methode aanroepen kan dan als volgt:

ToonArray(leeftijden);

En de output zal dan zijn:

Array output:
2
5
1
6

Voorbeeldprogramma met methoden

Volgend programma toont hoe we verschillende onderdelen van de code in methoden hebben geplaatst zodat: 1. de lezer van de code sneller kan zien wat het programma juist doet 2. zodat code herbruikbaar is

Analyseer de code en denk na hoe eenvoudig het is om een ander programma hiervan te maken (bijvoorbeeld vermenigvuldigen met 10 en alle veelvouden van 6 tonen: je hoeft enkel de parameters in de methode-aanroep aan te passen):

namespace ArrayMethods
{
    class Program
    {

        static void VulArray(int[] getalArray)
        {
            for (int i = 0; i < getalArray.Length; i++)
            {
                getalArray[i] = i;
            }
        }

        static void VermenigvuldigArray(int[] getalArray, int multiplier)
        {
            for (int i = 0; i < getalArray.Length; i++)
            {
                getalArray[i] = getalArray[i] * multiplier;
            }
        }

        static void ToonVeelvouden(int[] getalArray, int veelvoudenvan)
        {
            for (int i = 0; i < getalArray.Length; i++)
            {
                if (getalArray[i] % veelvoudenvan == 0)
                    Console.WriteLine(getalArray[i]);
            }
        }

        static void Main(string[] args)
        {
            //Array aanmaken
            int[] getallen = new int[100];

            //Array vullen
            VulArray(getallen);

            //Alle elementen met 3 vermenigvuldigen
            VermenigvuldigArray(getallen, 3);

            //Enkel veelvouden van 4 op het scherm tonen
            ToonVeelvouden(getallen, 4);

        }
    }
}

Array als return-type bij een methode

Ook methoden kun je natuurlijk een array als returntype laten geven. Hiervoor zet je gewoon het type array als returntype zonder grootte in de methode-signature.

Stel bijvoorbeeld dat je een methode hebt die een int-array maakt van een gegeven grootte waarbij ieder element van de array reeds een beginwaarde heeft die je ook als parameter meegeeft:

static int[] MaakArray(int lengte, int beginwaarde)
{
    int[] resultArray = new int[lengte];
    for (int i = 0; i < lengte; i++)
    {
        resultArray[i] = beginwaarde;
    }
    return resultArray;
}

(Klassikale!) smaakmaker OOP

1. Keuzemenuutje voor latere programma's

Leerdoelen

  • Code makkelijk toegankelijk maken

  • Vermijden heleboel piepkleine projectjes aan te maken

Functionele analyse

We gaan leren werken met grotere hoeveelheden code. We zullen voortaan niet meer al onze programma's in een apart project plaatsen, maar een keuzemenu gebruiken om daaruit alle andere programma's op te starten.

Technische analyse

Wanneer de gebruiker de Main van de klasse Program opstart, krijg je een keuzemenuutje. Hierop worden omschrijvingen getoond voor alle oefeningen die je al hebt, gevolgd door de unieke titel van de oefening in kwestie. Wanneer de gebruiker het getal intypt dat overeenstemt met een oefening, wordt de oefening opgestart.

Voorbeeldoutput:

Wat wil je doen? 1. Vormen tekenen (h8-vormen) 2. Auto's laten rijden (h8-autos) 3. Patiënten tonen (h8-patienten) 4. Honden laten blaffen (h8-blaffende-honden)

Wanneer de gebruiker een van deze opties kiest door het juiste getal in te typen, wordt dat programma opgestart. Als hij iets intypt dat niet kan worden uitgevoerd, komt er een boodschap ongeldig getal!. Wanneer je dit programma voor het eerst schrijft, kan je alleen nog maar ongeldige keuzes maken.

2. Kennismaking OOP (h8-vormen)

  • We werken niet in een map voor het vak "Programmeren 3", maar in een map "OOP" (zoals de namespace die we hanteren, maar dat is geen regel die C# oplegt).

  • Indien je reeds een project met de naam OOP hebt, hoef je géén nieuw project aan te maken.

  • Je gebruikt ShapesBuilder.cs in plaats van Vormen.cs en schrijft in het algemeen je code in het Engels.

  • Je gebruikt de namespace OOP.Geometry in plaats van Wiskunde.Meetkunde.

  • Als je van stap 6 naar stap 7 gaat, laat je de code voor de eigenschap Color staan.

  • Later zullen we deze code refactoren om nog meer gebruik te maken van objecten.

3. Voorbeeld met auto's (h8-autos)

Doelstelling

  • Kennismaking met OOP

Functionele analyse

We bouwen een simulatie van rijdende auto's. We voorzien deze van een kilometerteller, een huidige snelheid, een methode om de gaspedaal te simuleren en om de rem te simuleren.

Technische analyse

  • We starten vanaf de klasse Auto uit de cursus (maar noemen deze Car).

  • We voegen eigenschappen Odometer en Speed toe, beide doubles.

  • We zorgen dat de snelheid nooit onder de 0km/u kan gaan en nooit boven de 120km/u door de eigenschap aan te passen.

  • We schrijven klassikaal een methode Gas() die de snelheid met 10km/u verhoogt en de auto verplaatst met zijn gemiddelde snelheid / 60 (om één minuut rijden te simuleren). De gemiddelde snelheid is het gemiddelde van de snelheid voor en na het verhogen.

  • We schrijven individueel een methode Brake die gelijkaardig werkt, maar die de snelheid met 10 km/u verlaagt.

  • We schrijven een methode Main

    • die twee auto's aanmaakt

    • de eerste vijf keer laat versnellen, drie keer laat remmen

    • de tweede tien keer laat versnellen en een keer laat remmen

    • in onderstaand formaat de snelheid en afgelegde weg afprint: auto 1: km/u, afgelegde weg km auto 2: km/u, afgelegde weg km

4. Refactoren van deze code (met allerlei arrays voor eigenschappen) die een verslag produceert met info (h8patienten)

Doelstelling

  • Kennismaking met OOP

  • Kennismaking met refactoring

  • Toepassing van encapsulatie

Functionele analyse

We hebben al wat code, maar we vinden deze moeilijk te onderhouden omdat we allerlei variabelen moeten onderhouden. We refactoren deze om zo een meer objectgericht en beter onderhoudbaar programma te bekomen.

Technische analyse

  • Introduceer een klasse Patient

  • Vervang alle arrays door één array van objecten van de klasse Patient

  • Zorg dat het afgeprinte verslag er identiek uitziet aan het oorspronkelijke verslag

Code om van te starten:

using System; 

namespace OOP {     
   class PatientProgram {         
      public static void Main() {             
         Console.WriteLine("Hoe veel patiënten zijn er?"); // aanmaken van de nodige variabelen
         int numberOfPatients = int.Parse(Console.ReadLine());
         string[] patientNames = new string[numberOfPatients];
         string[] patientGenders = new string[numberOfPatients];
         string[] patientLifestyles = new string[numberOfPatients];
         int[] patientDays = new int[numberOfPatients];
         int[] patientMonths = new int[numberOfPatients];
         int[] patientYears = new int[numberOfPatients];

         // invullen van de rijen
         for(int i = 0; i < numberOfPatients; i++) {
            patientNames[i] = Console.ReadLine();
            patientGenders[i] = Console.ReadLine();
            patientLifestyles[i] = Console.ReadLine();
            patientDays[i] = int.Parse(Console.ReadLine());
            patientMonths[i] = int.Parse(Console.ReadLine());
            patientYears[i] = int.Parse(Console.ReadLine());
         }

         // afprinten van het verslag
         // variabele in twee keer toegekend om code niet te breed te maken
         for(int i = 0; i < numberOfPatients; i++) {
            string info = $"{patientNames[i]} ({patientGenders[i]}, {patientLifestyles[i]})";
            info += $", geboren {patientDays[i]}-{patientMonths[i]}{patientYears[i]}";
            Console.WriteLine(info);
         }
      }
   }
}

5. Voorbeeld abstractie (h8-honden)

Doelstelling

  • Kennismaking met OOP Kennismaking met refactoring

  • Toepassing van abstractie

Functionele analyse

We krijgen een programma dat al objecten bevat, maar dit programma moet zelf nog veel rekening houden met hoe deze objecten in elkaar zitten. We refactoren het om zo een meer objectgericht en beter onderhoudbaar programma te bekomen.

Technische analyse

Je krijgt volgende twee files. De bestandsnamen volgen de afspraak:

using System; 

namespace OOP {
   class BarkingProgram {
      // nu maken we onze randomgenerator *buiten* Main
      public static Random rng = new Random();
      public static void Main() {
         BarkingDog dog1 = new BarkingDog();
         BarkingDog dog2 = new BarkingDog();
         dog1.Name = "Swieber";
         dog2.Name = "Misty";
         dog1BreedNumber = rng.Next(0,3);
         dog2BreedNumber = rng.Next(0,3);
         if(dog1BreedNumber == 0) {
            dog1.Breed = "German Shepherd";
         }             
         else if(dog1BreedNumber == 1) {
            dog1.Breed = "Wolfspitz";
         }
         else {
            dog1.Breed = "Chihuahua";
         }
         if(dog2BreedNumber == 0) {
            dog2.Breed = "German Shepherd";
         }
         else if(dog2BreedNumber == 1) {
            dog2.Breed = "Wolfspitz";
         }
         else {
            dog2.Breed = "Chihuahua";
         }
         while(true) {
            Console.WriteLine(dog1.Bark());
            Console.WriteLine(dog2.Bark());
         }
      }
   }
}

en

namespace OOP {
    class BarkingDog {
        public string Name;
        public string Breed;

        public string Bark() {
            if(Breed == "German Shepherd") {
                return "RUFF!";
            }
            else if(Breed == "Wolfspitz") {
                return "AwawaWAF!";
            }
            else if(Breed == "Chihuahua") {
                return "ARF ARF ARF!";
            }
            // dit zou nooit mogen gebeuren
            // maar als de programmeur van Main iets fout doet, kan het wel
            else {
                return "Euhhh... Miauw?";
            }
        }
    }
}

Volg hierbij volgende stappen:

  • Maak de random generator statisch onderdeel van de klasse BarkingDog.

  • Voeg volgende code toe binnen de klasse BarkingDog:

public BarkingDog() {
    // bepaal hier met de randomgenerator het ras van de hond
    // stel hier `Breed` in
    // maak van `Breed` een eigenschap zoals we dat bij de geometrische vormen hebben gedaan
}

6. Controleren dat alles kan worden opgestart via keuzemenu.

7. Alles in Git plaatsen en delen met de lector. Eerst controleren dat juiste files genegeerd worden met git status -u!

DateTime: leren werken met objecten

DateTime

De .NET klasse DateTime is de ideale manier om te leren werken met objecten. Het is een nuttige en toegankelijk klasse.

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

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 currentTime = DateTime.Now;
        Console.WriteLine(currentTime);

Met constructor

Enkele voorbeelden:

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 methoden

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

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

  • AddMinutes

  • AddMonths

  • AddSeconds

  • AddTicks

  • AddYears

Een voorbeeld:

DateTime timeNow= DateTime.Now;

DateTime nextWeek= timeNow.AddDays(7);

(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:

DateTime someTime= new DateTime(2019, 4, 1);

//much later...
someTime = someTime.AddYears(10);
Console.WriteLine(someTime);

DateTime properties

Properties zijn een zeer uniek aspect van C# zoals we in vorig hoofdstuk zagen. We zullen deze nog tot in den treuren leren maken. Via properties kan je de interne staat van objecten uitlezen én aanpassen, dit op een gecontroleerde manier.

Het fijne aan properties is dat :

  • je gebruikt ze alsof het gewone variabelen zijn

  • maar ze werken als methoden

Meer hierover later.

Enkele nuttige properties van DateTime zijn:

  • Date

  • Day

  • DayOfWeek

  • DayOfYear

  • Hour

  • Millisecond

  • Minute

  • Month

  • Second

  • Ticks

  • TimeOfDay

  • Today

  • UtcNow

  • Year

Properties gebruiken

Alle properties van DateTime zijn read-only.

Een voorbeeld:

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;

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

DateTime now = DateTime.Now;

Console.WriteLine($"The current day is {now.DayOfWeek}");

Datum en tijd formateren

Je hebt een invloed op hoe DateTime objecten naar string worden opgezet. Je kan dit door door extra formatter syntax mee te geven.

Dit zie je in volgende voorbeeld:

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

Custom format

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

Localized time

De manier waarop DateTime objecten worden getoond (via ToString) is afhankelijk van de landinstellingen van je systeem. Soms wil je echter op een andere manier dit 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 aanmaakt en dat je dan meegeeft:

DateTime now = DateTime.Now;
CultureInfo russianCI = new CultureInfo("ru-RU");

Console.WriteLine($"Current time in Russian style is: {now.ToString("F", russianCI)}");

Culture names

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

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.

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:

string date_string = "8/11/2016"; //dit zou dus ook door gebruiker kunnen ingetypt zijn
DateTime dt = DateTime.Parse(date_string);
Console.WriteLine(dt);

Zoals je ziet roepen we Parse aan op DateTime en dus niet op een specifiek object.

IsLeapYear

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

DateTime today= DateTime.Now;
bool isLeap= DateTime.IsLeapYear(today.Year);
if(isLeap==true)
    Console.WriteLine("This year is a leap year");

TimeSpan (PRO)

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

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);

Oefening

Klokje

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.

Verjaardag

Maak een applicatie die aan de gebruiker vraagt op welke dag hij jarig is. Toon vervolgens over hoeveel dagen z'n verjaardag dan zal zijn.

Constructors

Constructors

Werking new operator

Objecten die je aanmaakt komen niet zomaar tot leven. Nieuwe objecten maken we aan met behulp van de new operator zoals we al gezien hebben:

De new operator doet 2 dingen:

  • Het maakt een object aan in het geheugen

  • Het roept de operator van het object aan voor eventuele extra initialisatie

Via de constructor van een klasse kunnen we extra code meegeven die moet uitgevoerd worden telkens een nieuw object van dit type wordt aangemaakt.

De constructor is een unieke methode die wordt aangeroepen bij het aanmaken van een object, daarom dat we ronde haakjes zetten bij new Student().

Soorten constructors

Als programmeur van eigen klassen zijn er 3 opties voor je:

  • Je gebruikt geen constructors: het leven gaat voort zoals het is. Je kunt objecten aanmaken zoals eerder getoond.

  • Je hebt enkel een default constructor nodig. Je kan nog steeds objecten met new Student() aanmaken, maar je gaat zelf beschrijven wat er moet gebeuren bij de default constructor

  • Je wenst gebruik te maken van een of meerdere overloaded constructoren, hierbij zal je dan extra argumenten kunnen meegeven bij de creatie van een object, bijvoorbeeld: new Student(24, "Jos").

Constructors zijn soms gratis, soms niet

Een lege default constructor voor je klasse krijg je standaard wanneer je een nieuwe klasse aanmaakt. Je ziet deze niet en kan deze niet aanpassen. Je kan echter daarom altijd objecten met new myClass() aanmaken.

Van zodra je echter beslist om zelf een of meerdere constructors te schrijven zal C# zeggen "ok, jij je zin, nu doe je alles zelf". De default constructor die je gratis kreeg zal ook niet meer bestaan en heb je die dus nodig dan zal je die dus zelf moeten schrijven!

Default constructor

De default constructor is een constructor die geen extra parameters aanvaardt. Een constructor bestaat ALTIJD uit volgende vorm:

  • Heeft geen returntype, ook niet void.

  • Heeft als naam de naam van de klasse zelf.

Stel dat we een klasse Student hebben:

We willen telkens een Student-object wordt aangemaakt dat deze een random leeftijd heeft. Via de default constructor kunnen we dat oplossen (je kan namelijk niet schrijven private int age = random.Next(10,20) )

Eerst schrijven de default constructor, deze ziet er als volgt uit:

Zoals verteld moet de constructor de naam van de klasse hebben, public zijn en geen returntype definiëren.

Vervolgens voegen we de code toe die we nodig hebben:

Dit is in veel programmeertalen slecht gebruik van Random, maar we hebben nog niet de nodige achtergrond om de juiste werkwijze te tonen. Dat komt binnenkort!

Telkens we nu een object zouden aanmaken met new Student() zal deze een willekeurige leeftijd hebben.

Opmerking bij voorgaande code

  • Als je op een gelijkaardige manier in andere programmeertalen twee of meerdere Studenten snel na mekaar aanmaakt zullen deze dezelfde leeftijd hebben. Dit is omdat ieder object z'n eigen Random aanmaakt en zoals we weten zal een random generator dezelfde getallen genereren als deze vlak na mekaar (in tijd) zijn aangemaakt. Een oplossing zullen we hier later voor zien. Spoiler, static is de oplossing hiervoor:

Constructor met parameter(s)

Soms wil je argumenten aan een object meegeven bij creatie. We willen bijvoorbeeld de leeftijd meegeven die het object moet hebben bij het aanmaken. Met andere woorden, stel dat we dit willen schrijven:

Als we dit met voorgaande klasse , die enkel een default constructor heeft, uitvoeren zal de code een fout geven. C# vindt geen constructor die een int als parameter aanvaardt.

Dat was eenvoudig. Maar denk eraan: je hebt een eigen constructor geschreven en dus heeft C# gezet "ok, je schrijft zelf constructor, trek je plan. Maar de default zal je ook zal moeten schrijven!" Je kan nu enkel je objecten met new Student(25) aanmaken. Schrijf je new Student() dan zal je een error krijgen. Wil je die constructor nog hebben, dan zal je die met de hand moeten schrijven, bijvoorbeeld:

Wanneer heb ik constructoren nodig?

Tot zeer recent maakten we onze objecten steeds aan met de default constructor. Pas daarna gaven we eventuele properties de juiste waarde. Dat houdt een risico in: er is een periode waarin onze objecten nog niet "af" zijn. In het slechtste geval vergeten we zelfs om de properties in te stellen en krijgen we objecten die misschien ongeldig zijn.

Constructoren helpen dit probleem te voorkomen. Als we één constructor hebben, bijvoorbeeld Student(string name), moeten we die gebruiken. We kunnen dus niet vergeten bijvoorbeeld frankVermeulen.Name = "Frank Vermeulen" te schrijven, want we worden gedwongen meteen new Student("Frank Vermeulen") te schrijven.

Samengevat: als er eigenschappen zijn die je meteen bij het aanmaken van een object wil instellen, maak er dan parameters van een constructor voor.

Overloaded constructoren

Wil je meerdere overloaded constructors, dan mag dat ook. Je wilt misschien een constructor die de leeftijd vraag alsook een bool om mee te geven of het om een werkstudent gaat:

Constructor chaining

Als je meerdere overloaded constructoren hebt, hoef je niet in elke constructor alle code voor objectinitialisatie te schrijven. Het sleutelwoordje this biedt de mogelijkheid eerst een andere constructor aan te roepen en eventueel andere operaties toe te voegen. Dit heet constructor chaining. In bovenstaand voorbeeld kan je ook dit schrijven:

Kennisclip

Null en NullReferenceException

Null en NullReferenceException

Zoals nu duidelijk is bevatten variabelen steeds een referentie naar een object. Maar wat als we dit schrijven:

Dit zal een fout geven. stud1 bevat namelijk nog geen referentie. Maar wat dan wel?

Deze variabele bevat de waarde null . Net zoals bij value types die een default waarde hebben (bv 0 bij een int ) als je er geen geeft, zo bevat reference types altijd null.

NullReferenceException

Een veel voorkomende foutboodschap tijdens de uitvoer van je applicatie is de zogenaamde NullReferenceException . Deze zal optreden wanneer je code een object probeert te benaderen wiens waarde null is.

Laten we dit eens simuleren:

Dit zal resulteren in volgende foutboodschap:

We moeten in dit voorbeeld expliciet =null plaatsen daar Visual Studio slim genoeg is om je te waarschuwen voor eenvoudige potentiele NullReference fouten en je code anders niet zal compileren.

NullReferenceException voorkomen

Objecten die niet bestaan zullen altijd null. Uiteraard kan je niet altijd al je code uitvlooien waar je misschien =new SomeObject(); bent vergeten.

Voorts kan het ook soms by design zijn dat een object voorlopig null is.

Gelukkig kan je controleren of een object null is als volgt:

Return null

Uiteraard mag je dus ook expliciet soms null teruggeven als resultaat van een methode. Stel dat je een methode hebt die in een array een bepaald object moet zoeken. Wat moet de methode teruggeven als deze niet gevonden wordt? Inderdaad, we geven dan null terug.

Volgende methode zoekt in een array van studenten naar een student met een specifieke naam en geeft deze terug als resultaat. Enkel als de hele array werd doorlopen en er geen match is wordt er null teruggegeven (de werking van arrays van objecten worden later besproken):

Kennisclip

Oefeningen

Richtlijnen

Structuur oefeningen

Vanaf hier veronderstellen we dat je in één groot project werkt dat één klasse Program heeft. De oefeningen worden los van elkaar omschreven, maar je zorgt ervoor dat ze getest kunnen worden via het keuzemenu in je klasse Program.

meerdere Main methodes

Indien je meerdere klassen hebt met een methode Main, moet je aangeven welke normaal gebruikt wordt voor de uitvoering van je programma. Dat doe je door aan je .csproj-bestand het volgende toe te voegen: <StartupObject>Namespace-van-de-opstartklasse.Naam-van-de-opstartklasse</StartupObject>

Dit staat hier niet zomaar! Als je dit niet doet, zullen je oefeningen niet uitgevoerd kunnen worden. Je .csproj file staat in dezelfde map als je .cs-bestanden en indien het niet opent in Visual Studio, kan je het openen in Kladblok.

Bijvoorbeeld:

(Je file zou er al gelijkaardig moeten uitzien, maar je moet zelf het StartupObject toevoegen.)

Oefening: H8-dag-van-de-week

Leerdoelen

  • aanmaken van DateTime objecten

  • formatteren van DateTime objecten

Functionele analyse

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

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 het gaat

    • gebruik hiervoor formattering van een DateTime

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

    • als je computer niet volledig ingesteld is op Belgisch Nederlands, kan het resultaat er wat anders uitzien.

  • noem de klasse waarin je dit schrijft DayOfWeekProgram

    • schrijf je code in de statische methode Main

    • roep de statische Main van DayOfWeekProgram op in het keuzemenu van Program

Voorbeeldinteractie

Oefening: H8-ticks-sinds-2000

Leerdoelen

  • aanmaken van DateTime objecten

Functionele analyse

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

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 klasse waarin je dit schrijft Ticks2000Program

Voorbeeldinteractie

Oefening: H8-schrikkelteller

Leerdoelen

  • gebruik van een statische property

Functionele analyse

We willen bepalen hoe veel schrikkeljaren er zijn tussen 1800 en 2020.

Technische analyse

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

  • maak gebruik van een statische methode van deze klasse

  • noem je klasse LeapYearProgram en voorzie ze van een Main

Voorbeeldinteractie

Oefening: H8-simpele-timing

Leerdoelen

  • eenvoudig code leren timen

  • gebruiken van DateTime

  • herhaling arrays

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,...

Technische analyse

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

  • Vul de array in met een for-lus.

  • Noem de klasse waarin je dit schrijft ArrayTimerProgram

Voorbeeldinteractie

Oefening: H8-RapportModule-V1

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van simpele properties en methodes

Functionele analyse

Dit programma geeft op basis van de input van een percentage de graad weer die je met dit gegeven zou behaald hebben.

Technische analyse

Ontwerp een klasse ResultV1 die je zal tonen wat je graad is gegeven een bepaald behaald percentage. Het enige dat je aan een ResultV1-object moet kunnen geven is het behaalde percentage. Enkel het totaal behaalde percentage wordt bijgehouden. Via een methode PrintHonors kan de behaalde graad worden weergegeven. Dit zijn de mogelijkheden:

  • < 50: niet geslaagd;

  • tussen 50 en 68: voldoende;

  • tussen 68 en 75: onderscheiding;

  • tussen 75 en 85: grote onderscheiding;

  • > 85: grootste onderscheiding.

Je hoeft voorlopig geen rekening te houden met ongeldige waarden. Test je klasse door ResultV1 ook van een statische methode Main te voorzien, waarin je enkele objecten van deze klasse aanmaakt en de verschillende properties waarden te geeft en vervolgens PrintHonors oproept.

Oefening: H8-RapportModule-V2

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van simpele properties en methodes

Functionele analyse

Dit programma geeft op basis van de input van een percentage de graad weer die je met dit gegeven zou behaald hebben.

Technische analyse

Ontwerp een klasse ResultV2 die je zal vertellen wat je graad is gegeven een bepaald behaald percentage. Het enige dat je aan een ResultV2-object moet kunnen geven is het behaalde percentage. Enkel het totaal behaalde percentage wordt bijgehouden. Via een methode ComputeHonors kan de behaalde graad worden teruggegeven. Dit werkt op dezelfde manier als in versie 1 van deze oefening, maar de verschillende graden worden voorgesteld met een Enum, Honors. De methode ComputeHonors toont het resultaat niet, maar geeft een waarde van deze Enum terug. Het is aan de Main om deze waarde af te printen, zodat je kan zien of je code werkt.

Test je klasse op dezelfde manier als versie 1. De teruggegeven waarden van Honors mag je in de Main meegeven aan Console.WriteLine.

Oefening: H8-Getallencombinatie

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties

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 dit woordelijk weergegeven.

Technische analyse

Maak een eenvoudige klasse NumberCombination. 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: geeft product van beide getallen weer

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

Gebruik full properties voor Number1 en Number2 en toon in je Main aan dat je code werkt.

Volgende code zou namelijk onderstaande output moeten geven:

Voorbeeldinteractie(s)

Oefening: H8-Figuren

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

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.

Technische analyse

Er is een klasse Rectangle met properties Width en Height en een klasse Triangle met Base en Height. Je programma maakt de figuren die hierboven beschreven worden aan met waarde 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, Surface van het type double.

Indien om het even welk van deze properties wordt ingesteld op 0 (of minder) wordt er een foutboodschap afgeprint en wordt de afmeting niet aangepast.

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

Schrijf de voorbeelden uit in de Main van een extra klasse, FigureProgram.

Voorbeeldinteractie(s)

(Er worden twee rechthoeken en twee driehoeken aangemaakt. De afmetingen van de eerste rechthoek worden eerst op -1 en 0 ingesteld.

Oefening: H8-Studentklasse

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

Functionele analyse

Dit programma vraagt om de naam en leeftijd van een student. Er moet ook worden meegeven in welke klasgroep de student is ingeschreven. De groepen zijn EA1, EA2 en EB1. Vervolgens worden de punten voor 3 vakken gevraagd, waarna het gemiddelde wordt teruggegeven.

Technische analyse

Maak een nieuwe klasse Student. Deze klasse heeft 6 properties. Leeftijd en de punten stel je voor met full properties. Een student kan nooit ouder zijn dan 120. Je kan ook nooit een cijfer boven 20 behalen. Over leeftijden en cijfers onder 0 hoef je je geen zorgen te maken, want de achterliggende variabelen zijn bytes en die zijn altijd minstens 0.

  • Name (string)

  • Age (byte)

  • ClassGroup (maak hiervoor een enum ClassGroups)

  • MarkCommunication (byte)

  • MarkProgrammingPrinciples (byte)

  • MarkWebTech (byte)

Voeg aan de klasse een read-only property OverallMark toe. Deze berekent het gemiddelde van de 3 punten als double.

Voeg aan de klasse ook de methode ShowOverview() toe. Deze methode zal een volledig rapport van de student tonen (inclusief het gemiddelde m.b.v. de OverallMark-property).

Test je programma door enkele studenten aan te maken en in te stellen. Volgende statische methode Main zet je in de klasse Student.

Voorbeeldinteractie(s)

Oefening: H8-RapportModule-V3

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

Functionele analyse

Deze is gelijkaardig aan de vorige versie van het programma, maar gebruikt iets geavanceerdere technieken.

Technische analyse

Maak een nieuwe versie van H8-RapportModule-V2, waarbij je een full property gebruikt voor het percentage. Zorg ervoor dat dit steeds tussen 0 en 100 ligt. Vervang ook ComputeHonors door een read-only property, Honors.

Oefeningen

Oefening 1: klasse Pokemon (h9-pokeattack)

Leerdoelen

  • herhalen aanmaken klassen

  • herhalen aanmaken instantiemethoden

  • herhalen aanmaken full properties

Functionele analyse

We willen een bootleg Pokémon spel maken. We starten met een klasse om Pokémon voor te stellen.

Technische analyse

Schrijf een klasse Pokemon met volgende onderdelen:

  • een full property MaxHP

    • deze stelt een getal voor dat altijd minstens 20 en maximum 1000 bedraagt

    • als je een lagere of hogere waarde probeert in te stellen, wordt de dichtstbijzijnde waarde ingesteld

  • een full property HP

    • deze stelt een getal voor dat altijd groter dan of gelijk aan 0 is; verder kan de waarde ook nooit groter gemaakt worden dan MaxHP; elke poging om het getal kleiner dan 0 te maken, maakt het gelijk aan 0 en elke poging om boven MaxHP te gaan, maakt het gelijk aan MaxHP.

  • een autoproperty PokeSpecies om aan te geven over welk soort Pokémon het gaat; maak hiervoor een enum PokeSpecies met waarden Bulbasaur, Charmander, Squirtle, Pikachu

  • een autoproperty PokeType om aan te geven wat het element van de Pokémon is; maak hiervoor een enum PokeTypes met waarden Grass, Fire, Water, Electric

  • een methode Attack(): deze zorgt ervoor dat de naam van het soort Pokémon in hoofdletters en in kleur wordt geprint. Je kan de methode ToString() van een enum gebruiken. De kleur die je gebruikt is als volgt:

    • groen voor type Grass

    • rood voor type Fire

    • blauw voor Water

    • geel voor Electric

Schrijf dan een statische MakePokemon-methode in de klasse Pokemon die één Pokemon van elke soort maakt (je mag de soort en het type invullen na het aanmaken van de objecten) en elk van deze Pokemon één keer hun methode Attack laat uitvoeren. Elke Pokemon start bovendien met 20 hit points als huidige waarde en als maximumwaarde.

Voorbeeldinteractie

(In groen, rood, blauw en geel.)

Oefening 2 (h9-consciouspokemon)

Leerdoelen

  • arrays van objecten

  • null

Functionele analyse

In een gevecht begin je met je eerste Pokémon die nog bij bewustzijn is. Bewusteloze Pokémon kunnen immers niet vechten. Schrijf een statische methode om de eerste bewuste Pokémon te vinden.

Technische analyse

Schrijf een statische methode FirstConsciousPokemon met één parameter: een array van Pokemon. Deze methode loopt met een for-lus door de array en geeft als antwoord de eerste Pokemon terug met minstens 1 HP. Je moet zorgen dat de methode aanvaard wordt door de compiler, ook als er geen enkele bewuste Pokémon in de rij is bijgehouden.

Schrijf ook een statische methode TestConsciousPokemon() die een array van dezelfde vier Pokémon als hierboven maakt, waarbij Bulbasaur en Charmander 0 HP hebben en Squirtle 2 HP. Toon wat gebeurt als de eerste wakkere Pokémon aanvaalt. Dit is de methode die je vanuit je keuzemenu zal oproepen.

Voorbeeldinteractie

Oefening 3 (h9-consciouspokemon-improved)

Leerdoelen

  • arrays van objecten

  • null

Functionele analyse

Je moet ook het geval afhandelen waarbij al je Pokémon KO zijn.

Technische analyse

Breid je methode TestConsciousPokemon uit zodat ze niet crasht wanneer al je Pokémon KO zijn. Doe dit in een nieuwe versie, TestConsciousPokemonSafe.

Voorbeeldinteractie

Oefening 4: value en reference (h9-pokevalueref)

Leerdoelen

  • call by value vs. call by reference

Functionele analyse

Een beginnend programmeur bij Game Freak heeft volgende statische methode geschreven in je klasse:

Hij gaat ervan uit dat dit werkt:

Maar dit klopt niet. Los zijn bug op.

Technische analyse

Je moet RestoreHP anders schrijven en ook het gebruik ervan aanpassen. Je mag de parameters van RestoreHP volledig aanpassen en ook de eerste for-lus veranderen. De tweede for-lus en het aanmaken van de array van Pokemon moeten exact gebeuren zoals ze geschreven zijn.

Roep DemoRestoreHP() op uit je keuzemenu.

Voorbeeldinteractie

Oefening 5: uitkomst gevecht (h9-fight)

Leerdoelen

  • gebruik van Random

  • null guard

  • call by reference

Functionele analyse

Hoe wreed het ook is, Pokémon zijn bestemd om tegen elkaar te vechten. Schrijf een simulatie van een gevecht met een willekeurig element.

Technische analyse

Schrijf eerst een enumeratie FightOutcome met drie mogelijkheden: WIN, LOSS en UNDECIDED ("onbeslist").

Schrijf dan een statische methode FightOutcome in de klasse Pokemon. Deze heeft drie parameters, twee Pokemon-objecten en één Random-object.

FightOutcome() werkt als volgt:

  • Een van de twee Pokémon mag eerst aan de beurt; welke van de twee wordt willekeurig beslist met behulp van het Random-object.

  • Wanneer een Pokémon aan de beurt is, voert hij zijn Attack() methode uit.

  • Hierna verlaagt de HP van de andere Pokémon met een getal tussen 0 en 20.

  • Hierna is de andere van de twee Pokémon aan de beurt, maar alleen als hij nog bij bewustzijn is.

  • De match is voorbij wanneer één van de twee Pokémon 0 HP heeft bereikt. Dan wordt het resultaat teruggegeven:

    • WIN als de eerste Pokémon die je als parameter hebt meegegeven nog bij bewustzijn is.

    • LOSS als de tweede nog bij bewustzijn is.

Handel ook situaties af waarbij minstens één van de twee Pokémon null is of al KO is bij het begin van de match. Dan wint de andere vanzelf, tenzij ze allebei ontbreken of KO zijn. Dan is de uitkomst UNDECIDED.

Schrijf ten slotte een methode DemoFightOutcome() die twee Pokémon naar keuze aanmaakt, hen tegen elkaar laat vechten tot er een resultaat is en dat resultaat dan op het scherm toont.

Test je methode met alle combinaties:

  • twee gezonde Pokémon

  • één bewusteloos

  • twee bewusteloos

  • één null

  • twee null

  • één bewusteloos en één null

Afronden

Ga na dat al je code van de eerste oefeningen nog werkt nadat je de laatste hebt afgerond en plaats alles op Bitbucket.

Methoden en access modifiers

Een eenvoudige klasse

We zullen nu enkele basisconcepten van klassen en objecten toelichten aan de hand van praktische voorbeelden.

Object methoden

Stel dat we een klasse willen maken die ons toelaat om objecten te maken die verschillende mensen voorstellen. We willen aan iedere mens kunnen vragen waar deze woont en die zal dat dan op het scherm tonen.

We maken een nieuwe klasse Mens en maken een methode Praat die iets op het scherm zet:

We zien twee nieuwe aspecten:

  • Voor de methode plaatsen we public : dit leggen we zo meteen uit

Je kan nu elders objecten aanmaken en ieder object z'n methode Praat aanroepen:

Er zal 2x Ik ben een mens! op het scherm verschijnen. Waarbij telkens ieder object (joske en alfons) zelf verantwoordelijk ervoor waren dat dit gebeurde.

Public en private access modifiers

De access modifier geeft aan hoe zichtbaar een bepaald deel van de klasse is. Wanneer je niet wilt dat "van buitenuit" (bv objecten van je klasse in de Main) 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 eens wat gebeurt wanneer je public verwijderd voor de methode. Inderdaad, je zal de methode Praat niet meer op de objecten kunnen aanroepen.

De reden: wanneer je voor een methode (of klasse) niet expliciet public zet, dan kan deze methode niet van uit alle andere klassen worden aangeroepen.

Test volgende klasse eens, kan je de methode VertelGeheim vanuit de Main op joske aanroepen?

Als je duidelijk wil maken dat bepaalde code niet van buitenaf aangeroepen kan worden, schrijf dan private in plaats van public. Als je geen van beide schrijft, zit je code ergens tussenin public en private (zie later). Als beginnende programmeur maak je er best een gewoonte van duidelijk te kiezen voor public of private.

Reden van private

Waarom zou je bepaalde zaken private maken?

Stel dat we in de klasse extra (hulp)methoden willen gebruiken die enkel intern nodig zijn, dan doen we dit. Volgende voorbeeld toont hoe we in de methode Praat de private methode VertelGeheim gebruiken:

Als we nu elders een object laten praten als volgt:

Dan zal de uitvoer worden:

Instantievariabelen

We maken onze klasse wat groter, we willen dat een object een leeftijd heeft die we kunnen verhogen via een methode VerjaardagVieren (we hebben de methode VertelGeheim even weggelaten):

Hier zien we een pak details die onze aandacht vereisen:

  • Ook variabelen zoals int leeftijd mogen een access modifier krijgen in een klasse. Ook hier, als je niet expliciet public zet wordt deze als private beschouwd.

  • Ook al is leeftijd private alle methoden in de klasse kunnen hier aan. Het is enkel langs buiten dat bijvoorbeeld volgende code niet zal werken rachid.leeftijd = 34;.

  • We kunnen iedere variabele een beginwaarde geven.

  • Ieder object zal z'n eigen leeftijd hebben.

Die laatste opmerking is een kernconcept van OOP: ieder object heeft z'n eigen interne staat die kan aangepast worden individueel van de andere objecten van hetzelfde type.

We zullen dit testen in volgende voorbeeld waarin we 2 objecten maken en enkel 1 ervan laten verjaren. Kijk wat er gebeurt:

Als je deze code zou uitvoeren zal je zien dat de leeftijd van Elvis verhoogt en niet die van Bono wanneer we VerjaardagVieren aanroepen. Zoals het hoort!

Initiële waarde van een instantievariabele

Bekijk opnieuw het voorbeeld:

De eerste regel binnenin de klasse betekent dat een Mens wordt aangemaakt met een leeftijd van 1 jaar. WE noemen dit de initiële waarde van de instantievariabele leeftijd. Het is niet verplicht deze te voorzien. Als je niet aangeeft welke waarde een variabele krijgt (hier of in, zoals we iets verder zullen zien, de constructor), dan zal de instantievariabele een defaultwaarde krijgen die afhangt van zijn type.

Dus hebben we nu eigenlijk een object met properties en methoden. Zo zit een object in programmeertalen er ook uit. Een mail object heeft bijvoorbeeld als property de ontvanger, de verzender en het bericht en als methode versturen. Dit zijn natuurlijk erg vereenvoudigde voorbeelden en in de praktijk zal het er een stuk ingewikkelder aan toe gaan maar de basisprincipes zijn steeds hetzelfde. .

Steve Jobs, de oprichter van Apple, was een fervent fan van OOP. In een interview, way back, gaf hij volgende uitstekende uitleg:

Klasse toevoegen in VS
basics oop same in vv

Een aantal oefeningen is geïnspireerd op oefeningen van .

Volgende opgave komt uit

Maak een spel , vergelijkbaar als galgje, waarin de speler een woord moet raden. Zie voor de spelregels indien je deze niet kent.

De enige manier om FirstName een waarde te geven is via de constructor van de klasse. Alle andere manieren zal een error genereren.

Doorloop de instructies en uitleg op , maar let op volgende zaken:

Grote delen van deze tekst komen van .

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

Een lijst van alle cultures in .NET kan je .

Dit semester is iedere constructor altijd public ()

kunnen we ook constructors overloaden. De code is verrassen gelijkaardig als bij method overloading:

Het keyword static komt bij een klasse niet aan te pas (of toch minder zoals we )

Bron van deze introductie
bron
Bron
Exercism.io
Originele bron voor dit idee
Exercism.io
Wiki
Meer info.
Hier een iets meer uitgebreid PRO antwoord.
Properties
Full properties
Auto properties
ModernWays
zetoode.com
hier kan zien
Dit wordt hier volledig uit de doeken gedaan.
hier terugvinden
Student FrankVermeulen = new Student();

private int leeftijd= 35;
class Student
{
    private int age;
}
class Student
{
    public Student()
    {
        // zet hier de code die bij initialisatie moet gebeuren
    }

    private int age;
}
class Student
{
    public Student()
    {
        Random r= new Random();
        age= r.Next(10,20);
    }

    private int age;
}
class Student
{
    static Random r= new Random();
    public Student()
    {
        age= r.Next(10,20);
    }

    private int age;
}
Student jos= new Student(19);
class Student
{
    public Student(int startage)
    {
        age= startage
    }

    private int age;
}
class Student
{
    public Student(int startage) // met parameter
    {
        age= startage;
    }

    public Student() //default
    {
        Random r= new Random();
        age= r.Next(10,20);
    }

    private int age;
}
class Student
{
    public Student(int startage) //overloaded
    {
        age= startage;
    }

    public Student(int startage, bool werkstart) //overloaded
    {
        age= startage;
        isWerkStudent= werkstart;
    }

    public Student() //default
    {
        Random r= new Random();
        age= r.Next(10,20);
    }

    private int age;
    private bool isWerkStudent
}
class Student
{
    public Student(int startage) : this(startage, false)
    {
    // niet nodig hier code uit uitgebreidere constructor te herhalen
    // hier kan nog extra code worden uitgevoerd na de oproep van de uitgebreide constructor
    }

    public Student(int startage, bool werkstart) //overloaded
    {
        age= startage;
        isWerkStudent=werkstart;
    }

    public Student() //default
    {
        // helaas wordt onderstaande code pas **na** de chained constructor opgeroepen
    // gebruik van this heeft hier dus niet veel zin
        Random r= new Random();
        age= r.Next(10,20);
    }

    private int age;
    private bool isWerkStudent
}
Student stud1;
stud1.Naam= "Test";
Student stud1 = null;

Console.WriteLine(stud1.Name);
if(stud1 == null)
    Console.WriteLine("oei. object bestaat niet")
static Student ZoekStudent(Student[] array, string naam)
{
    for (int i = 0; i < array.Length; i++)
    {
        if (array[i].Name == naam)
            return array[i];
    }

    return null;
}
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <StartupObject>OOP.Program</StartupObject>
  </PropertyGroup>
</Project>
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 1800 en 2020.
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.
NumberCombination pair1 = new NumberCombination();
pair1.Number1 = 12;
pair1.Number2 = 34;
Console.WriteLine("Paar:" + pair1.Number1 + ", " + pair1.Number2);
Console.WriteLine("Sum = " + pair1.Sum());
Console.WriteLine("Verschil = " + pair1.Difference());
Console.WriteLine("Product = " + pair1.Product());
Console.WriteLine("Quotient = " + pair1.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².
Student student1= new Student();
student1.Class = ClassGroups.EA2;
student1.Age = 21;
student1.Name = "Joske Vermeulen";
student1.MarkCommunication = 12;
student1.MarkProgrammingPrinciples = 15;
student1.MarkWebTech = 13;
student1.ShowOverview();
Joske Vermeulen, 21 jaar
Klas: EA2

Cijferrapport:
**********
Communicatie:             12
Programming Principles:   15
Web Technology:           13
Gemiddelde:               13.3
BULBASAUR!
CHARMANDER!
SQUIRTLE!
PIKACHU!
SQUIRTLE!
Al je Pokémon zijn KO! Haast je naar het Pokémon center.
public static void RestoreHP(int oldHP, int newHP) {
    oldHP = newHP;
}
public static void DemoRestoreHP() {
// aanmaken van array bewusteloze Pokemon van 4 soorten zoals eerder: zelf doen
for(int i = 0; i < pokemon.Length; i++) {
    Pokemon.RestoreHP(pokemon[i].HP,pokemon[i].MaxHP);
    }
}
for(int i = 0; i < pokemon.Length; i++) {
    Console.WriteLine(pokemon[i].HP)
    }
}
20
20
20
20
class Mens
{
    public void Praat()
    {
        Console.WriteLine("Ik ben een mens!");
    }
}
Mens joske = new Mens();
Mens alfons = new Mens();

joske.Praat();
alfons.Praat();
class Mens
{
    public void Praat()
    {
        Console.WriteLine("Ik ben een mens!");
    }

    void VertelGeheim()
    {
        Console.WriteLine("Mijn geheim is dat ik leef!");
    }
}
class Mens
{
    public void Praat()
    {
        Console.WriteLine("Ik ben een mens!");
        VertelGeheim();
    }

    void VertelGeheim()
    {
        Console.WriteLine("Mijn geheim is dat ik leef!");
    }
}
Mens rachid = new Mens();
rachid.Praat();
Ik ben een mens!
Mijn geheim is dat ik leef!
class Mens
{
    private int leeftijd = 1;

    public void VerjaardagVieren()
    {
        Console.WriteLine("Hiphip hoera voor mezelf!");
        leeftijd++;
        Praat();
    }

    public void Praat()
    {
        Console.WriteLine("Ik ben een mens! ");
        Console.WriteLine($"Ik ben {leeftijd} jaar oud");
    }

}
Mens Elvis = new Mens();
Mens Bono = new Mens();

Elvis.VerjaardagVieren();
Elvis.VerjaardagVieren();
Evlis.VerjaardagVieren();
Bono.VerjaardagVieren();
class Mens
{
    private int leeftijd = 1;

    public void VerjaardagVieren()
    {
        Console.WriteLine("Hiphip hoera voor mezelf!");
        leeftijd++;
        Praat();
    }

    public void Praat()
    {
        Console.WriteLine("Ik ben een mens! ");
        Console.WriteLine($"Ik ben {leeftijd} jaar oud");
    }

}

Spelen met strings

We gebruiken het einde van klassen en objecten om iets dieper in de String klasse te duiken en aan te tonen dat er tal van nuttige zaken bestaan om met strings te werken.

Verbatim character

Door een @ (verbatim character) voor een string te plaatsen zeggen we concreet: "de hele string die nu volgt moet je beschouwen zoals hij er staat. Je mag alle escape karakters negeren.

Dit wordt vaak gebruikt om een filepath iets leesbaarder te maken.

  • Zonder verbatim: string path= "c:\\Temp\\myfile.txt";

  • Met verbatim: string path= @"c:\Temp\myfile.txt";

Splitsen en samenvoegen

Split

Aan het einde van dit hoofdstuk willen we een csv-bestand (comma separated value) splitsen. De Split methode laat toe een string te splitsen op een bepaald teken. Het resultaat is steeds een array van strings.

string data= "12,13,20";
string[] gesplitst= data.Split(',');

for(int i=0; i<gesplitst.Length;i++)
{
    Console.WriteLine(gesplitst[i]);
}

Uiteraard kan je dit dus gebruiken om op eender welk char te splitsen en dus nit enkel een ',' (komma).

Join

Via Join kunnen we array van string terug samenvoegen. Het resultaat is een nieuwe string.

Volgende voorbeeld zal de eerder array van het vorige voorbeeld opnieuw samenvoegen maar nu met telkens een ; tussen iedere string:

string joined = String.Join(";", gesplitst);

Voorgaande methoden zijn static en moet je dus via de klasse String doen en niet via de objecten zelf.

Andere nuttige methoden

Volgende methoden/properties kan je rechtstreeks op de string-objecten aanroepen (i.e. niet static methoden)

  • int Length: geeft totaal aantal karakters in de string

  • IndexOf(string para): geeft int terug die de index bevat waar de string die je als parameter meegaf begint

  • Trim(): verwijdersd alle onnodige spaties vooraan en achteraan de string en geeft de opgekuiste string terug

  • ToUpper(): zal de meegegeven string naar ALLCAPS omzetten en geeft de nieuwe string als resultaat terug

  • ToLower(): idem maar dan naar kleine letters

  • Replace(string old, string news): zal in de string alle substring die gelijk zijn aan old vervangen door de meegegeven news string

Strings vergelijken

De correcte manier om strings te vergelijken is met de Compare(string s1, string s2) methode. Deze zal een int terug geven:

  • -1 : de string s1 komt voor de strings2 indien je ze lexicografisch zou sorteren (dit is ongeveer hetzelfde als alfabetisch, maar maakt bijvoorbeeld een onderscheid tussen kleine letters en hoofdletters).

  • 0: beide strings zijn identiek

  • 1: de string s2 komt voor s1

Tekst files uitlezen

De System.IO namespace bevat tal van nuttige methoden en klassen om met bestanden te werken. Om gebruik hiervan te maken plaats je bovenaan je file:

using System.IO;

Via System.File.ReadAllLines() kunnen we een tekstbestand uitlezen. De methode geeft een array van string terug. Per lijn die eindigt met een newline (\r\n) zal een nieuwe string aan de array toegevoegd worden.

string[] lines = File.ReadAllLines(@"c:\mypoem.txt");
for (int i = 0; i < lines.Length; i++)
{
    Console.WriteLine(lines[i]);
}

CSV uitlezen

Dankzij ReadAllLines en Split hebben we nu alle bouwstenen om eenvoudig een csv-bestand te verwerken.

Stel je voor dat een bestand soccer.csv volgende tekst bevat:

Dams;Tim;1981
Hamdaoui;Mounir;1984
Stoffels;José;1950

Volgende code zal dit bestand uitlezen en de individuele data op het scherm tonen:

string[] lines = File.ReadAllLines(@"c:\soccerstars.csv");
for (int i = 0; i < lines.Length; i++)
{
    string[] splitted = lines[i].Split(';');

    Console.WriteLine($"Voornaam speler {i}= {splitted[1]}" );
    Console.WriteLine($"Achternaam speler {i}= {splitted[0]}");
    Console.WriteLine($"Geboortejaar speler {i}= {splitted[2]}");
}

CSV downloaden

Vaak zal je een online bestand willen verwerken. De WebClient klasse heeft tal van manieren om met online bronnen te werken. Deze klasse bevindt zich in de System.Net namespace en vereist dus dat je bovenaan je code volgende lijn toevoegt:

using System.Net

Volgende code toont hoe we een bestand van een specifieke locatie kunnen downloaden:

WebClient wc = new WebClient();
string csv = wc.DownloadString("www.fakeaddress.com/mydata.csv");

Dit bestand is 1 platte tekst. Willen we deze vervolgens verwerken dan moeten we deze splitsen in lijnen:

string[] split = csv.Split('\n');

We hebben nu een for nodig die lijn per lijn zal splitsen:

for (int i = 1; i < splitted.Length; i++)
{
    string[] lijnsplit = splitted[i].Split(',');
    Console.WriteLine("Data 1="+lijnsplit[0]);
    Console.WriteLine("Data 2=" + lijnsplit[1]);
}

In dit voorbeeld gaan we er vanuit dat de eerste lijn in het bestand een "header" bevat, die we dus moeten overslaan. Daarom starten we de loop vanaf lijn 1.

CSV wegschrijven

Je kan tekst uit een bestand lezen, maar uiteraard kan je ook naar een bestand wegschrijven. De 2 eenvoudigste manieren zijn:

  • File.WriteAllText: deze gebruik je als je 1 enkele string wilt wegschrijven

  • File.WriteAllLines: deze is de omgekeerde van ReadAllLines() en zal een array van strings wegschrijven.

Een voorbeeld:

string[] stringArray = new string[]
    {
        "cat",
        "dog",
        "arrow"
    };


File.WriteAllLines("file.txt", stringArray);

Opgelet met het schrijven naar bestanden: dit zal onherroepelijk het target bestand overschrijven. .Gebruik if(File.Exists(pathtofile)) om te controleren of een bestand bestaat of niet. Eventueel kan je dan aan de gebruiker bevestiging vragen of je deze effectief wilt overschrijven.

Wil je CSV-bestand maken dan zal je eerst met String.Join een komma-separated lijst maken, bijvoorbeeld:

string[] namen = { "Tim", "Jos", "Mo" };
int[] leeftijden = { 34, 76, 23 };

string[] lines = new string[namen.Length];
for (int i = 0; i < lines.Length; i++)
{
    lines[i] = $"{i},{namen[i]},{leeftijden[i]}";

}

System.IO.File.WriteAllLines("ages.csv", lines);

Static

Static

Je hebt het keyword static al een paar keer zien staan voor methoden het vorige semester. En dit semester werd er dan weer nadrukkelijk verteld géén static voor methoden te plaatsen. Wat is het nu?

Een static onderdeel van een klasse hoort bij de klasse zelf en niet bij specifieke instanties van die klasse. Je hoeft dus geen instanties aan te maken om gebruik te maken van dit onderdeel.

static kan op 2 manieren gebruikt worden:

  1. Bij variabelen om een gedeelde variabele aan te maken, over de objecten van die klasse heen.

  2. Bij methoden om zogenaamde methoden-bibliotheken of hulpmethoden aan te maken.

Variabelen en het static keyword

Zonder het keyword heeft ieder object z'n eigen variabelen en aanpassingen binnen het object aan die variabelen heeft geen invloed op andere objecten. We tonen eerst de werking zoals we gewend zijn en vervolgens hoe static werkt.

Variabelen ZONDER static

Gegeven volgende klasse:

class Mens
{
    private int leeftijd=1;
    public void Jarig()
    {
        leeftijd++;
    }

    public void ToonLeeftijd()
    {
        Console.WriteLine(leeftijd);
    }
}

Als we dit doen:

Mens m1= new Mens();
Mens m2= new Mens();

m1.Jarig();
m1.Jarig();

m2.Jarig();

m1.ToonLeeftijd();
m2.ToonLeeftijd();

Dan zien we volgende uitvoer:

3
2

Ieder object houdt de stand van z'n eigen variabelen bij. Ze kunne mekaars interne (zowel publieke als private) staat niet veranderen.

Variabelen MET static

We maken de variabele private int leeftijd static als volgt: private static int leeftijd=1;.

We krijgen dan:

class Mens
{
    private static int leeftijd=1;
    public void Jarig()
    {
        leeftijd++;
    }

    public void ToonLeeftijd()
    {
        Console.WriteLine(leeftijd);
    }
}

We hebben nu gezegd dat ALLE objecten de variabele leeftijd delen. Er wordt van deze variabele dus maar een "instantie" in het geheugen gemaakt.

Voeren we nu terug volgende code uit:

Mens m1= new Mens();
Mens m2= new Mens();

m1.Jarig();
m1.Jarig();

m2.Jarig();

m1.ToonLeeftijd();
m2.ToonLeeftijd();

Dan wordt de uitvoer:

4
4

Static laat je dus toe om informatie over de objecten heen te delen. Gebruik static niet te pas en te onpas: vaak druist het in tegen de concepten van OO en wordt het vooral misbruikt Ga je dit vaak nodig hebben? Niet zo vaak. Het volgende concept wel.

Methoden met static

Heb je er al bij stil gestaan waarom je dit kan doen:

Math.Pow(3,2);

Zonder dat we objecten moeten aanmaken in de trend van:

Math myMath= new Math(); //dit mag niet!
myMath.Pow(3,2)

De reden dat je de math-bibliotheken kan aanroepen rechtsreeks op de klasse en niet op objecten van die klasse is omdat de methoden in die klasse als static gedefineerd staan.

Voorbeeld van static methoden

Stel dat we enkele veelgebruikte methoden willen groeperen en deze gebruiken zonder telkens een object te moeten aanmaken dan doen we dit als volgt:

class EpicLibray
{
    static public void ToonInfo()
    {
        Console.WriteLine("Ik ben ik");
    }

    static public int TelOp(int a, int b)
    {
        return a+b;
    }
}

We kunnen deze methoden nu als volgt aanroepen:

EpicLibrary.ToonInfo();

int opgeteld= EpicLibrary.TelOp(3,5);

Mooi toch.

Nog een voorbeeld

In het volgende voorbeeld gebruiken we een static variabele om bij te houden hoeveel objecten (via de constructor) er van de klasse reeds zijn aangemaakt:

class Fiets
{
    private static int aantalFietsen = 0;
    public Fiets()
    {
        aantalFietsen++;
        Console.WriteLine($"Er zijn nu {aantalFietsen} gemaakt");
    }

    public static void VerminderFiets()
    {
        aantalFietsen--;
    }
}

Merk op dat we de methoden VerminderFiets enkel via de klasse kunnen aanroepen:

Fiets.VerminderFiets();

private static?

Merk op dat, in bovenstaand voorbeeld, aantalFietsen private is en dat er geen publieke property aanwezig is, terwijl een constructor wel bij een instantie hoort. Herinner je ook dat private onderdelen van een klasse alleen toegankelijk zijn vanuit code binnenin die klasse. De code voor objecten van een bepaalde klasse staat ook binnenin die klasse, dus private static onderdelen van een klasse zijn toegankelijk voor de objecten van die klasse!

Static vs non-static

Van zodra je een methode hebt die static is dan zal deze methode enkel andere `static methoden en variabelen kunnen aanspreken. Dat is logisch: een static methode heeft geen toegang tot de gewone niet-statische variabelen van een individueel object, want welk object zou hij dan moeten aanpassen?

Volgende code zal dus een error geven:

class Mens
{
    private int gewicht=50;

    private static void VerminderGewicht()
    {
        gewicht--;
    }
}

De error die verschijnt An object reference is required for the non-static field, method, or property 'Program.Fiets.gewicht' zal bij de lijn gewicht-- staan.

Een eenvoudige regel is te onthouden dat van zodra je in een static omgeving (meestal een methode) bent je niet meer naar de niet-static delen van je code zal geraken.

Static en main

Dit verklaart ook waarom je bij console applicaties in Program.cs steeds alle methoden static moet maken. Een console-applicatie is als volgt beschreven wanneer je het aanmaakt:

public class Program
{
        public static void Main()
        {

        }
}

Zoals je ziet is de Main methode als static gedefinieerd. Willen we dus vanuit deze methode andere methoden aanroepen dan moeten deze als static aangeduid zijn.

Uiteraard kan je wel niet-static zaken gebruiken en daarom kan je dus gewone objecten etc. in je static methoden aanmaken.

meer info
Net zoals bij overloading van methoden
Constructors intro en default constructor
Referenties en null
later zullen zien

Oefeningen

Pokémons makkelijk aanmaken (h10-pokeconstructie)

Leerdoelen

  • gebruik van expliciete constructoren

Functionele analyse

Zorg ervoor dat elke Pokémon bij constructie zijn eigenschappen krijgt.

Technische analyse

  • Voorzie je klasse Pokémon (uit hoofdstuk 9) van een constructor zonder parameters. Dit om bestaande code intact te houden.

  • Voorzie je klasse Pokémon (uit hoofdstuk 9) van een constructor met vier parameters, één per property. Pas je methode MakePokemon van hoofdstuk 9 aan zodat gebruik wordt gemaakt van deze constructor in plaats van de constructor zonder argumenten.

Voorbeeldinteractie

(Hier is geen verschil met hoe dit er in hoofdstuk 9 uitzag.)

Pokémons nog makkelijker aanmaken (h10-chaining)

Leerdoelen

  • gebruik van expliciete constructoren

  • herbruik van bestaande constructor

Functionele analyse

Wanneer we een willekeurige Pokémon aanmaken, start deze normaal met de helft van zijn maximale hit points. We willen onszelf het werk besparen van dit elke keer uit te typen, dus we voorzien een extra constructor.

Technische analyse

Maak een nieuwe constructor met drie argumenten in plaats van vier. Het argument voor HP valt weg. Deze nieuwe constructor maakt eerst gebruik van de bestaande constructor met vier argumenten. Daarna past hij in zijn body de hoeveelheid HP aan naar de helft van het maximum. Bij het gebruik van de meer algemene constructor maakt het niet uit welke waarde je meegeeft voor de huidige hit points. Test je constructor uit met een (statische) demomethode ConstructPokemonChained(). Deze maakt met deze nieuwe constructor een nieuwe Pokémon (naar keuze) aan met een maximale hoeveelheid HP naar keuze en toont de uitkomst.

Voorbeeldinteractie

De nieuwe Squirtle heeft maximum 40 HP en heeft momenteel 20 HP.

Globale statistieken bijhouden (h10-pokebattlecount)

Leerdoelen

  • verschil tussen klasse en object

Functionele analyse

We willen bijhouden hoe vaak elk element al gebruikt is voor een aanval. Voeg code toe die bijhoudt hoe vaak een Pokémon van type Grass al heeft aangevallen, hoe vaak een Pokémon van type Fire al heeft aangevallen, etc. Voeg ook een statische methode, DemonstrateCounter() aan die vijf willekeurige Pokémon aanmaakt en elke Pokémon tussen de 5 en de 10 keer laat aanvallen.

Technische analyse

Bij een aanval van een Pokémon met een bepaald type, gaat er een teller omhoog. Er is één teller per PokéType.

Voorbeeldinteractie

BULBASAUR!
BULBASAUR!
BULBASAUR!
BULBASAUR!
BULBASAUR!
SQUIRTLE!
SQUIRTLE!
SQUIRTLE!
SQUIRTLE!
SQUIRTLE!
PIKACHU!
PIKACHU!
PIKACHU!
PIKACHU!
PIKACHU!
CHARMANDER!
CHARMANDER!
CHARMANDER!
CHARMANDER!
CHARMANDER!
CHARMANDER!
BULBASAUR!
BULBASAUR!
BULBASAUR!
BULBASAUR!
BULBASAUR!
BULBASAUR!
BULBASAUR!
Aantal aanvallen van Pokémon met type `Grass`: 12
Aantal aanvallen van Pokémon met type `Fire`: 6
Aantal aanvallen van Pokémon met type `Electric`: 4
Aantal aanvallen van Pokémon met type `Water`: 5

(Merk op: er zijn twee Bulbasaurs in het spel, maar alle aanvallen van graspokémon zijn samen geteld.)

Gemeenschappelijke kenmerken (h10-tombola)

Leerdoelen

  • goed gebruik van Random

  • static

  • constructoren

Functionele analyse

We schrijven een digitale tombola. Iedere keer een lotje wordt aangemaakt, wordt er een willekeurig getal aan toegekend.

Technische analyse

Maak een klasse, Ticket. Deze is voorzien van één autoproperty: Prize. Dit is een byte. Bij aanmaak van een Ticket wordt deze property ingesteld op een waarde tussen 1 en 100. Schrijf je code zodat dezelfde Random gebruikt wordt voor alle tickets. Je kan dus geen Random aanmaken iedere keer je een Ticket aanmaakt! Maak ook een methode Raffle (d.w.z. "tombola") om te demonstreren dat dit werkt. Deze methode maakt een rij met 10 lotjes aan en print de waarde van elk lotje in de rij. Het is niet erg dat twee lotjes dezelfde waarde kunnen krijgen.

Voorbeeldinteractie

Waarde van het lotje: 77
Waarde van het lotje: 8
Waarde van het lotje: 12
Waarde van het lotje: 14
Waarde van het lotje: 51
Waarde van het lotje: 97
Waarde van het lotje: 20
Waarde van het lotje: 15
Waarde van het lotje: 32
Waarde van het lotje: 68

CSV

Objecten en methoden

Objecten als argumenten

Klassen zijn "gewoon" nieuwe types. Alle regels die we dus al kenden in verband met het doorgeven van variabelen als parameters in een methoden blijven gelden. Het enige verschil is dat we objecten by reference meegeven aan een methode. Aanpassingen aan het object in de methode zal dus betekenen dat je het originele object aanpast dat aan de methode werd meegegeven. Hier moet je dus zeker rekening mee houden.

Een voorbeeld. Stel dat we volgende klasse hebben waarin we metingen willen opslaan, alsook wie de meting heeft gedaan:

class Meting
{
    public int Temperatuur { get; set; }
    public string OpgemetenDoor { get; set; }
}

In ons hoofdprogramma schrijven we een methode ToonMetingInKleur die ons toelaat om deze meting op het scherm te tonen in een bepaalde kleur. Het gebruik en de methode zelf zouden er zo kunnen uitzien:

static void Main(string[] args)
{
    Meting m1 = new Meting();
    m1.Temperatuur = 26; 
    m1.OpgemetenDoor = "Elon Musk";
    Meting m2 = new Meting();
    m2.Temperatuur = 34; 
    m2.OpgemetenDoor = "Dennis Rodman";

    ToonMetingInKleur(m1, ConsoleColor.Red);
    ToonMetingInKleur(m2, ConsoleColor.Gray);
}

static void ToonMetingInKleur (Meting inmeting, ConsoleColor kleur)
{
    Console.ForegroundColor = kleur;
    Console.WriteLine($"Temperatuur {inmeting.Temperatuur}°C werd opgemeten door {inmeting.OpgemetenDoor}");
    Console.ResetColor();
}

Objecten in methoden aanpassen

Je kan dus ook methoden schrijven die meegegeven objecten aanpassen daar we deze by reference doorsturen. Een voorbeeld:

static void ToonMetingEnVerhoog(Meting inmeting)
{
    ToonMetingInKleur(inmeting, ConsoleColor.Green);

    inmeting.Temperatuur++;
}

Als we deze methode als volgt aanroepen:

Meting m1 = new Meting();
m1.Temperatuur = 26; m1.OpgemetenDoor = "Elon Musk";

ToonMetingEnVerhoog(m1);

Console.WriteLine(m1.Temperatuur);

Dan zullen we zien dat de temperatuur in m1 effectief met 1 werd verhoogd.

Dit gedrag zouden we NIET zien bij volgende methode daar int by value wordt doorgegeven:

static void VerhoogGetal(int inmeting)
{
    inmeting++;
}

Onderdelen van objecten als parameter

Stel dat we volgende methode hebben

static double Gemiddelde(double getal1, double getal2)
{
    return (getal1 + getal2) / 2;
}

Je mag deze methode dus ook oproepen als volgt (we gebruiken de Meting objecten m1 en m2 uit vorige paragraaf):

double result= Gemiddelde(m1.Temperatuur, m2.Temperatuur);

Het type van de property Temperatuur is int en mag je dus als parameter aan deze methoden meegeven.

Objecten als resultaat

Weer hetzelfde verhaal: ook klassen mogen het resultaat van een methoden zijn.

static Meting GenereerRandomMeting()
{
    Meting result = new Meting();
    Random r = new Random();
    result.Temperatuur = r.Next(-100, 200);
    result.OpgemetenDoor = "Onbekend";

    return result;
}

Deze methode kan je dan als volgt gebruiken:

Meting m3 = GenereerRandomMeting();

Merk op dat het dus kan zijn dat een methode null teruggeeft. Het kan dus zeker geen kwaad om steeds in je code te controleren of je effectief iets hebt terug gekregen:

Meting m3 = GenereerRandomMeting();
if(m3 != null)
{
    ToonMetingInKleur(m3, ConsoleColor.Red);
}

Kennisclip

Arrays van objecten

Object arrays

We vallen in herhaling: ook arrays van objecten zijn mogelijk, net zoals je arrays van valuetypes al kon maken. Ook hier is de werking grotendeels dezelfde. Maar ook hier moet je er rekening mee houden dat de individuele objecten in je array reference values hebben en dus mogelijk null zijn.

Array van objecten aanmaken

Een array van objecten gebeurt als volgt:

Student[] mijnKlas = new Student[20];

Maar: er staan nog géén objecten in deze array. Alle elementen in deze array zijn nu nog null. Je zou kunnen zeggen dat we enkel nog maar de parkeerlijnen hebben aangemaakt.

Willen we nu elementen in deze array plaatsen dan moeten dit ook expliciet doen:

mijnKlas[0]= new Student();
mijnKlas[7]= new Student();

Uiteraard kan dit ook in een loop indien relevant voor de opgave.

Probeer je objecten te benaderen die nog niet bestaan dan zal je uiteraard een NullReferenceException krijgen.

Array initializer syntax

Je kan ook een variant op de object initializer syntax gebruiken waarbij de objecten reeds van bij de start in de array worden aangemaakt. Als extra'tje zorgt dit er ook voor dat we geen lengte moeten meegeven, de compiler zal deze zelf bepalen. Volgende voorbeeld maakt een nieuwe array aan die bestaat uit 2 nieuwe studenten, alsook 1 bestaande (jos):

Student jos=new Student();
Student[] mijnKlas = new Student[]
    {
        new Student(),
        new Student(),
        jos,
        new Student()
    };

Let op de kommapunt helemaal achteraan. Die wordt als eens vergeten.

Individueel object benaderen

Van zodra een object in de array staat kan je deze vanuit de array aanspreken d.m.v. de index :

mijnKlas[3].Name= "Vincent Lagasse";

Arrays als parameters en return

Ook arrays mag je als parameters en returntype gebruiken in methoden. De werking hiervan is identiek aan die van value-types.

Stack en Heap

Stack en Heap

Geheugenmanagement in C-Sharp

Tot nog toe lagen we er niet van wakker wat er achter de schermen van een C# programma gebeurde. We duiken nu dieper in wat er juist gebeurt wanneer we variabelen aanmaken.

Twee soorten geheugen

Wanneer een C# applicatie wordt uitgevoerd krijgt het twee soorten geheugen toegewezen dat het 'naar hartelust' kan gebruiken:

  1. Het kleine, maar snelle stack geheugen

  2. Het grote, maar tragere heap geheugen

Afhankelijk van het soort variabele wordt ofwel de stack, ofwel de heap gebruikt. Het is uitermate belangrijk dat je weet in welk geheugen de variabele zal bewaard worden!

Er zijn namelijk twee soorten variabelen:

  1. Value types

  2. Reference types

Als je volgende tabel begrijpt dan beheers je geheugenmanagement in C#:

Value types

Reference types

Inhoud van de variabele

De eigenlijke data

Een referentie naar de eigenlijke data

Locatie

(Data) Stack

Heap (globaal)geheugen

Beginwaarde

0,0.0, "",false, etc.

null

Effect van = operator

Kopieert de actuele waarde

Kopieert het adres naar de actuele waarde

Waarom twee geheugens?

Waarom plaatsen we niet alles in de stack? De reden hiervoor is dat bij het compileren van je applicatie er reeds zal berekend worden hoeveel geheugen de stack zal nodig hebben. Wanneer je programma dus later wordt uitgevoerd weet het OS perfect hoeveel geheugen het minstens moet reserveren. Er is echter een probleem: we kunnen niet alles perfect berekenen/voorspellen. Een variabele van het type int is perfect geweten hoe groot die zal zijn (32 bit).Maar wat met een string? Of met een array waarvan we pas tijdens de uitvoer de lengte aan de gebruiker misschien vragen? Het zou nutteloos (en zonde) zijn om reeds bij aanvang een bepaalde hoeveelheid voor een array te reserveren als we niet weten hoe groot die zal worden. Beeld je maar eens in dat we 2k byte reserveren om dan te ontdekken dat we maar 5byte ervan nodig hebben. RAM is goedkoop, maar toch... De heap laat ons dus toe om geheugen op een wat minder gestructureerde manier in te palmen. Tijdens de uitvoer van het programma zal de heap als het ware dienst doen als een grote zandbak waar eender welke plek kan ingepalmd worden om zaken te bewaren. De stack daarentegen is het kleine bankje naast de zandbak: handig, snel, en perfect geweten hoe groot.

Value types

Value types worden in de stack bewaard. De effectieve waarde van de variabele wordt in de stack bewaard. Dit zijn alle gekende, 'eenvoudige' datatypes die we totnogtoe gezien hebben, inclusief enums en structs (zie later):

  • sbyte, byte

  • short, ushort

  • int, uint

  • long, ulong

  • char

  • float, double, decimal

  • bool

  • structs (zien we niet in deze cursus)

  • enums

= operator bij value types

Wanneer we een value-type willen kopieren dan kopieren de echte waarde:

int getal=3;
int anderGetal= getal;

Vanaf nu zal anderGetal de waarde 3 hebben. Als we nu een van beide variabelen aanpassen dan zal dit geen effect hebben op de andere variabelen.

We zien het zelfde effect wanneer we een methode maken die een parameter van het value type aanvaardt- we geven een kopie van de variabele mee:

void DoeIets(int a)
{
    a++;
    Console.WriteLine($"In methode {a}");
}

//Elders:
int getal= 5;
DoeIets(getal);
Console.WriteLine($"Na methode {getal}");

De parameter a zal de waarde 5 gekopieerd krijgen. Maar wanneer we nu zaken aanpassen in a zal dit geen effect hebben op de waarde van getal. De output van bovenstaand programma zal zijn:

In methode 6
Na methode 5

Reference types

Reference types worden in de heap bewaard. De effectieve waarde wordt in de heap bewaard, en in de stack zal enkel een referentie of pointer naar de data in de heap bewaard worden. Een referentie (of pointer) is niet meer dan het geheugenadres naar waar verwezen wordt (bv 0xA3B3163) Concreet zijn dit alle zaken die vaak redelijk groot zullen zijn:

  • objecten, interfaces en delegates

  • arrays

= operator bij reference types

Wanneer we de = operator gebruiken bij een reference type dan kopieren we de referentie naar de waarde, niet de waarde zelf.

Bij objecten We zien dit gedrag bij alle reference types, zoals objecten:

Student stud= new Student();

Wat gebeurt er hier?

  1. new Student() : new roept de constructor van Student aan. Deze zal een constructor in de heap aanmaken en vervolgens de geheugenlocatie teruggeven.

  2. Een variabele stud wordt in de stack aangemaakt.

  3. De geheugenlocatie uit de eerste stap wordt vervolgens in stud opgeslagen in de stack.

Bij arrays

Maar ook bij arrays:

int[] nummers= {4,5,10};
int[] andereNummers= nummers;

In dit voorbeeld zal andereNummers dus nu ook verwijzen naar de array in de heap waar de actuele waarden staan.

Als we dus volgende code uitvoeren dan ontdekken we dat beide variabele naar dezelfde array verwijzen:

andereNummers[0]=999;
Console.WriteLine(andereNummers[0]);
Console.WriteLine(nummers[0]);

We zullen dus als output krijgen:

999
999

Hetzelfde gedrag zien we bij objecten:

Student a= new Student("Abba");
Student b= new Student("Queen");
a=b;
Console.WriteLine(a.Naam);

We zullen in dit geval dus Queen op het scherm zien omdat zowel b als a naar het zelfde object in de heap verwijzen. Het originele "abba"-object zijn we kwijt en zal verdwijnen (zie Garbage collector verderop).

Methoden en reference parameters

Ook bij methoden geven we de dus de referentie naar de waarde mee. In de methode kunnen we dus zaken aanpassen van de parameter en dan passen we eigenlijk de originele variabele aan:

void DoeIets(int[] a)
{
   a[0]++;
   Console.WriteLine($"In methode {a[0]}");
}

//Elders:
int[] getallen= {5,3,2};
DoeIets(getallen);
Console.WriteLine($"Na methode {getallen[0]}");

We krijgen als uitvoer:

In methode 6
Na methode 6

Opgelet: Wanneer we een methode hebben die een value type aanvaardt en we geven één element van de array met dan geven dus een kopie van de actuele waarde mee!

void DoeIets(int a)
{
    a++;
    Console.WriteLine($"In methode {a}");
}

//Elders:
int[] getallen= {5,3,2};
DoeIets(getallen[0]); //<= VALUE TYPE!
Console.WriteLine($"Na methode {getallen[0]}");

De output bewijst dit:

In methode 6
Na methode 5

De Garbage Collector (GC)

Een essentieel onderdeel van .NET is de zogenaamde GC, de Garbage Collector. Dit is een geautomatiseerd onderdeel van ieder C# programma dat ervoor zorgt dat we geen geheugen voor niets gereserveerd houden. De GC zal geregeld het geheugen doorlopen en kijken of er in de heap data staat waar geen references naar verwijzen. Indien er geen references naar wijzen zal dit stuk data verwijderd worden.

In dit voorbeeld zien we dit in actie:

int[] array1= {1,2,3};
int[] array2= {3,4,5};
array2=array1;

Vanaf de laatste lijn zal er geen referentie meer naar {3,4,5} in de heap zijn, daar we deze hebben overschreven met een referentie naar {1,2,3}.De GC zal dus deze data verwijderen.

Wil je dat niet dan zal je dus minstens 1 variabele moeten hebben dat naar de data verwijst. Volgende voorbeeld toont dit:

int[] array1= {1,2,3};
int[] array2= {3,4,5};
int[] bewaarArray= array2;
array2=array;

De variabele bewaarArray houdt dus een referentie naar {3,4,5} bij en we kunnen dus later via deze variabele alsnog aan de originele data.

Meer weten?

Meer info, lees zeker volgende artikels:

Kennisclip

Oefeningen

Oefening 1: prijzen met foreach (h11-prijzen)

Leerdoelen

  • foreach

  • combinatie controlestructuren

Functionele analyse

We willen enkele gegevens (prijzen) inlezen van de gebruiker en slechts sommige van deze prijzen tonen.

Technische analyse

  • Werk onderstaande opdracht uit in een statische methode AskForPrices.

  • Maak eerst een array die tot 20 prijzen (type double) kan bewaren.

  • Vraag aan de gebruiker om 20 prijzen in te voeren en bewaar elke prijs in de array.

  • Doorloop vervolgens m.b.v. een foreach-lus de volledige array.

    • Toon enkel de elementen op het scherm wiens prijs hoger of gelijk is aan €5.00.

  • Toon op het einde van het programma het gemiddelde van alle prijzen (dus inclusief de lagere prijzen).

  • Toon alles afgerond tot twee cijfers na de komma.

Voorbeeldinteractie

Oefening 2: speelkaarten (h11-speelkaarten)

Leerdoelen

  • foreach genest

  • List

Functionele analyse

We willen een kaartenspel programmeren. Om dat te doen, moeten we zeker een lijst kaarten kunnen aanmaken en ook kaarten te kunnen trekken.

Technische analyse

  • Maak een klasse PlayingCard.

    • Een kaart heeft 2 eigenschappen (properties)

      • een getal van 1 tot en met 13 (boer=11, koningin= 12, heer= 13):

      • een kleur, de zogenaamde "suite". Deze stel je voor via een enumtype Suites en kan als waarden Clubs (klaveren), Hearts (harten), Spades (schoppen) of Diamonds (ruiten) zijn.

  • Schrijf een statische methode GenerateDeck die een boek kaarten teruggeeft.

    • Schrijf om dit te bereiken twee foreach loops die de 52 kaarten van een standaard pak in een List<PlayingCard> plaatsen.

      • Doe dit door één lus in een andere te nesten.

  • Schrijf ook een statische methode ShowShuffledDeck(List<PlayingCard> cards) die telkens een willekeurige kaart uit de deck trekt en deze aan de gebruiker toont. De kaart wordt na het tonen dus uit de lijst verwijderd.

    • Doe dit door in iedere stap een willekeurige, geldige index in de lijst met kaarten te berekenen en die kaart uit de lijst te halen.

Voorbeeldinteractie

Oefening 3: organiseren van studenten (h11-organizer)

Functionele analyse

We gaan nu de Student-klasse uit een hoofdstuk 8 gebruiken om een List<Student> van studenten aan te maken. Daarna zullen we een menu tonen om gegevens over studenten in te voeren (student toevoegen, student aanpassen, gegevens over student tonen, student verwijderen). Op de werkvloer worden deze mogelijkheden "CRUD"-operaties genoemd (create, read, update, delete).

Technische analyse

  • Maak eerst (in de klasse Student) een statische methode ExecuteStudentMenu() zonder return type. Deze zal, zolang de gebruiker niet aangeeft dat hij wil stoppen, een menu tonen waarin we gegevens kunnen bekijken of wijzigen.

  • Het menu toont steeds volgende opties:

    1. gegevens van de studenten tonen

    2. een nieuwe student toevoegen

    3. gegevens van een bepaalde student aanpassen

    4. een student uit het systeem verwijderen

    5. stoppen

  • Je mag voorlopig veronderstellen dat de gebruiker geldige indexposities en gegevens invoert.

mogelijkheid 1: gegevens van de studenten tonen

Deze keuze toont, via de methode ShowOverview() en een foreach-lus, de gegevens van elke student in de lijst. Elk rapport wordt gevolgd door een lege regel. Het is niet erg als er op het einde één regel te veel is.

Voorbeeldinteractie

Nadat er al twee studenten zijn aangemaakt:

mogelijkheid 2: een nieuwe student toevoegen

Deze keuze voegt een nieuwe student toe.

Voorbeeldinteractie

Bij nul studenten:

mogelijkheid 3: een student aanpassen

Deze keuze staat toe de naam, leeftijd, klasgroep of een van de drie cijfers van de student aan te passen.

Voorbeeldinteractie

Vlak na het aanmaken van een eerste student:

mogelijkheid 4: een student verwijderen

Voorbeeldinteractie

Nadat er al twee studenten zijn aangemaakt:

Constructors bij overerving

Constructors bij overerving

Wanneer je een object instantiëert van een child-klasse dan gebeuren er meerdere zaken na elkaar, in volgende volgorde:

  • Eerst wordt de constructor aangeroepen van de basis-klasse: dus steeds eerst die van System.Object

  • Gevolgd door de constructors van alle parent-klassen

  • Finaal de constructor van de klasse zelf.

Volgende voorbeeld toont dit in actie:

Indien je vervolgens een object aanmaakt van het type Medic:

Dan zal zien we de volgorde van constructor-aanroep op het scherm:

Er wordt dus verondersteld in dit geval dat er een default constructor in de basis-klasse aanwezig is.

Overloaded constructors

Indien je klasse Soldier een overloaded constructor heeft, dan geeft deze niet automatisch een default constructor. Volgende code zou dus een probleem geven indien je een Medic wilt aanmaken via new Medic():

Wat je namelijk niet ziet bij child-klassen en hun constructors is dat er eigenlijk een impliciete call naar de basis-constructor wordt gedaan. Bij alle constructors staat eigenlijk :base() wat je ook zelf kunt schrijven:

base() achter de constructor zegt dus eigenlijk 'roep de constructor van de parent-klasse aan. Je mag hier echter ook parameters meegeven en de compiler zal dan zoeken naar een constructor in de basis-klasse die deze volgorde van parameters kan accepteren.

We zien hier dus hoe we ervoor moeten zorgen dat we terug Medics via new Medic() kunnen aanroepen zonder dat we de constructor(s) van Soldier moeten aanpassen:

De medics zullen de canShoot dus steeds op true zetten. Uiteraard wil je misschien dit kunnen meegeven bij het aanmaken van een object zoals new Medic(false), dit vereist dat je dus een overloaded constructor in Medic aanmaakt, die op zijn beurt de overloaded constructor van Soldier aanroept. Je schrijft dan een overloaded constructor in Medic bij:

Uiteraard mag je ook de default constructor aanroepen vanuit de child-constructor, alle combinaties zijn mogelijk (zolang de constructor in kwestie maar bestaat in de parent-klasse).

Kennisclip

Base keyword

Het base keyword laat ons toe om bij een overriden methode of property in de child-klasse toch te verplichten om de parent-implementatie toe te passen.

Stel dat we volgende 2 klassen hebben:

Het poetsen van een Frituur is duurder (1000 basis + 500 voor ontsmetting) dan een gewoon restaurant. Als we echter later beslissen dat de basisprijs (in Restaurant) moet veranderen dan moet je ook in alle child-klassen doen. Base lost dit voor ons. De Frituur-klasse herschrijven we naar:

Overerving intro

Overerving

Overerving (inheritance) laat ons toe om klassen te specialiseren vanuit een reeds bestaande basisklasse. Wanneer we een klasse van een andere klasse overerven dan zeggen we dat deze nieuwe klasse een child-klasse of sub-klasse is van de bestaande parent-klasse of super-klasse.

De child-klasse kan alles wat de parent-klasse kan, maar de nieuwe klasse kan nu ook extra specialisatie code krijgen.

Is-een relatie

Wanneer twee klassen met behulp van een "x is een y"-relatie kunnen beschreven worden dan weet je dat overerving mogelijk.

  • Een paard is een dier (paard = child-klasse, dier= parent-klasse)

  • Een tulp is een plant

Inheritance in CS

Overving duid je aan met behulp van het dubbele punt(:) bij de klassedefinitie:

Een voorbeeld:

Objecten van het type Dier kunnen enkel de Eet-methode aanroepen. Objecten van het type Paard kunnen de Eet-methode aanroepen én ze hebben ook een property KanHinnikken:

Multiple inheritance

In C# is het niet mogelijk om een klasse van meer dan een parent-klasse te laten overerven (zogenaamde multiple inheritance), wat wel mogelijk is in sommige andere object georiënteerde talen.

Transitive

Overerving in C# is transitief, dit wil zeggen dat de child-klasse niet alleen overerft van haar ouderklasse, maar ook van grootouderklassen enzovoort. Je zou bijvoorbeeld een subklasse Pony van de klasse Paard kunnen toevoegen. Een Pony is een Paard en een Paard is een Dier, dus een Pony is ook een Dier en heeft bijvoorbeeld een methode Eet.

Protected

Ook al heeft een subklasse alle onderdeeltjes van een ouderklasse, hou er rekening mee dat private variabelen en methoden van de parent-klasse NIET rechtsreeks aanroepbaar zijn in de child-klasse. Private betekent namelijk: "enkel toegankelijk binnenin deze klasse". Private geeft aan dat het element enkel in de klasse zichtbaar is:

Een Paard heeft nog wel een leeftijd, maar binnenin de code voor de klasse Paard kan je de leeftijd niet zomaar opvragen of aanpassen.

Je kan dit oplossen door de protected access modifier ipv private te gebruiken. Met protected geef je aan dat het element enkel zichtbaar is binnen de klasse en binnen child-klassen:

Sealed

Soms wil je niet dat van een klasse nog nieuwe klasse kunnen overgeërfd worden. Je lost dit op door het keyword sealed voor de klasse te zetten:

Als je later dan dit probeert:

zal dit resulteren in een foutoodschap, namelijk cannot derive from sealed type 'DoNotInheritMe'.

Kennisclip

Virtual en override

Virtual en Override

Soms willen we aangeven dat de implementatie (code) van een property of methode in een parent-klasse door child-klassen mag aangepast worden. Dit geven we aan met het virtual keyword:

Stel dat we 2 objecten aanmaken en laten vliegen:

De uitvoer zal dan zijn:

Een raket is een vliegtuig, toch vliegt het anders. We willen dus de methode Vlieg anders uitvoeren voor een raket. Daar hebben we override voor nodig. Door override voor een methode in de child-klasse te plaatsen zeggen we "gebruik deze implementatie en niet die van de parent klasse." Je kan enkel overriden indien de respectievelijke methode of property in de parent-klasse als virtual werd aangeduid

De uitvoer van volgende code zal nu anders zijn:

Uitvoer:

Properties overriden

Ook properties kan je virtual instellen en override'n.

Stel dat je volgende klasse hebt:

We maken nu een meer luxueuze auto die een lichtje heeft dat aangaat wanneer de benzine-tank vol genoeg is, dit kan via override.

Kennisclip

System.Object

System.Object

Alle klassen C# zijn afstammelingen van de System.Object klasse. Indien je een klasse schrijft zonder een expliciete parent dan zal deze steeds System.Object als rechtstreekse parent hebben. Ook afgeleide klassen stammen dus af van System.Object. Concreet wil dit zeggen dat alle klassen System.Object-klassen zijn en dus ook de bijhorende functionaliteit ervan hebben.

Because every class descends from Object, every object "is an" Object.

Indien je de System namespace in je project gebruikt door bovenaan using System; te schrijven dan moet je dus niet altijd System.Object schrijven maar mag je ook Object schrijven.

Hoe ziet System.Object er uit?

Wanneer je een lege klasse maakt dan zal je zien dat instanties van deze klasse reeds 4 methoden ingebouwd hebben, dit zijn uiteraard de methoden die in de System.Object klasse staan gedefinieerd:

GetType()

Stel dat je een klasse Student hebt gemaakt in je project. Je kan dan op een object van deze klasse de GetType() -methode aanroepen om te weten wat het type van dit object is:

Dit zal als uitvoer de namespace gevolgd door het type op het scherm geven. Als je project bijvoorbeeld "StudentManager" heet (en je namespace dus ook) dan zal er op het scherm verschijnen: StudentManager.Student.

Wil je enkel het type zonder namespace dan is het nuttig te beseffen dat GetType() een object teruggeeft van het type Type met meerdere eigenschappen, waaronder Name. Volgende code zal dus enkel Student op het scherm tonen:

ToString()

Deze is de nuttigste waar je al direct leuke dingen mee kan doen. Wanneer je schrijft:

Wordt je code eigenlijk herschreven naar:

Op het scherm verschijnt dan StudentManager.Student. Waarom? Wel, de methode ToString() wordt in System.Object() ongeveer als volgt beschreven:

Merk twee zaken op:

  1. GetType wordt aangeroepen en die output krijg je terug.

  2. De methode is virtual gedefinieerd.

    Alle 4 methoden in System.Object zijn virtual , en je kan deze dus override'n!

    ToString() overriden

    Het zou natuurlijk fijner zijn dat de ToString() van onze student nuttigere info teruggeeft, zoals bv de interne Naam (string autoprop) en Leeftijd (int autoprop). We kunnen dat eenvoudig krijgen door gewoon ToString to overriden:

    Wanneer je nu Console.WriteLine(stud1); (gelet dat hij een Naam en Leeftijd heeft) zou schrijven dan wordt je output: Student Tim Dams (Leeftijd:35).

Equals()

De Equals methode heeft dus als signatuur: public virtual bool Equals(Object o) Twee objecten zijn gelijk voor .NET als aan volgende afspraken wordt voldaan:

  • Het moet false teruggeven indien het argument o null is

  • Het moet true teruggeven indien je het object met zichzelf vergelijkt (bv stud1.Equals(stud1))

  • Het mag enkel true teruggeven als volgende statements beide waar zijn:

  • Indien stud1.Equals(stud2) true teruggeeft en stud1.Equals(stud3) ook true is, dan moet stud2.Equals(stud3) ook true zijn.

Equals overriden

Stel dat we vinden dat een student gelijk is aan een andere student indien z'n Naam en Leeftijd dezelfde is, we kunnen dan de Equals-methode overriden als volgt:

De lijn Student temp = (Student)o; zal het object o casten naar een Student. Doe je dit niet dan kan je niet aan de interne Student-variabelen van het object o.

GetHashcode

Indien je Equals override dan moet je eigenlijk ook GetHashCode overriden, daar er wordt verondersteld dat twee gelijke objecten ook dezelfde unieke hashcode teruggeven. Wil je dit dus implementeren dan zal je dus een (bestaand) algoritme moeten schrijven dat een uniek nummer genereert voor ieder niet-gelijke object.

Ik ben nog niet helemaal mee?

Kennisclip

Extra oefeningen

Oefening 4: gemiddelde cijfers opvragen per vak

Leerdoelen

  • foreach

  • combinatie controlestructuren

Functionele analyse

Pas je oefening met CRUD-operaties op Student aan zodat we ook het gemiddelde per vak kunnen opvragen.

Technische analyse

  • Je werkt nog steeds met een List<Student>.

  • Optie 5, 6 en 7 tonen nu het gemiddelde voor communicatie, programmeren of webtechnologie (in die volgorde).

  • Optie 8 stopt het menu.

Voorbeeldinteractie

Nadat er al drie studenten zijn aangemaakt, met 12, 17 en 19 op communicatie:

Oefening 5: hoger, lager

Leerdoelen

  • List

Functionele analyse

Gebruik je eerdere code voor PlayingCard om een spelletje "hoger", "lager" toe te voegen.

Technische analyse

  • Eerst wordt GenerateDeck gebruikt om een lijst aan te maken en toe te kennen aan een variabele.

  • Een willekeurig getal wordt gegenereerd tussen 0 en de maximale index van een element in de lijst.

  • De kaart op deze indexpositie wordt toegekend aan een variabele van type PlayingCard met naam previousCard.

  • Deze kaart wordt ook verwijderd uit de lijst.

  • Volgende stappen herhalen zich zo lang alle kaarten niet zijn gespeeld:

    • De waarde van previousCard wordt getoond.

    • Een willekeurig getal wordt gegenereerd tussen 0 en de maximale index van een element in de lijst.

    • De kaart op deze indexpositie wordt toegekend aan een variabele van type PlayingCard met naam currentCard.

    • Deze kaart wordt ook verwijderd uit de lijst.

    • De gebruiker krijgt de vraag of de waarde van currentCard hoger, lager of gelijk aan de waarde van previousCard is.

    • currentCard vervang previousCard

  • Het spel stopt als de gebruiker een fout maakt of als het spel kaarten op is.

  • Noem de methode om het spel op te starten HigherLower()

Voorbeeldinteractie

Foreach en var

Foreach loops

Wanneer je geen indexering nodig hebt, maar toch snel over alle elementen in een array wenst te gaan, dan is het foreach statement een zeer nuttig is. Een foreach loop zal ieder element in de array een voor een in een tijdelijke variabele plaatsen (de iteration variable). Volgende code toont de werking waarbij we een array van doubles hebben en alle elementen er in op het scherm willen tonen:

De eerste keer dat we in de loop gaan zal het element killdeathRates[0] aan kdrate toegewezen worden voor gebruik in de loop-body, vervolgens wordt killdeathRates[1] toegewezen, enz.

Het voordeel is dat je dus geen teller/index nodig hebt en dat foreach zelf de lengte van de array zal bepalen.

Opgelet bij het gebruik van foreach loops

  • De foreach iteration variable is read-only: je kan dus geen waarden in de array aanpassen, enkel uitlezen.

  • De foreach gebruik je enkel als je alle elementen van een array wenst te benaderen. In alle andere gevallen zal je een ander soort loop (for, while, etc.) moeten gebruiken.

var keyword

C# heeft een var keyword. Je mag dit keyword gebruiken ter vervanging van het type (bv int) op voorwaarde dat de compiler kan achterhalen wat het type moet zijn.

Opgelet: het var keyword is gewoon een lazy programmer syntax toevoeging om te voorkomen dat je als programmer niet constant het type moet schrijven

Bij javascript heeft var een totaal andere functie: het zegt eigenlijk "het type dat je in deze variabele kan steken is...variabel", m.a.w. het kan de ene keer een string zijn, dan een int. Bij C# gaat dit niet: eens je een variabele aanmaakt dan zal dat type onveranderbaar zijn.

var en foreach

Kennisclip

List

List

Een List<> collectie is de meest standaard collectie die je kan beschouwen als een veiligere variant op een een doodnormale array.

De Generieke List<> klasse bevindt zich in de System.Collections.Generic namespace. Je dient deze namespace dus als using bovenaan toe te voegen wil je deze klasse kunnen gebruiken.

List aanmaken

De klasse List<> is een zogenaamde generieke klasse. Tussen de < >tekens plaatsen we het type dat de lijst zal moeten gaan bevatten. Bijvoorbeeld:

  • List<int> alleGetallen= new List<int>();

  • List<bool> binaryList = new List<bool>();

  • List<Pokemon> pokeDex = new List<Pokemon>();

  • List<string[]> listOfStringarrays = new List<string[]>();

Zoals je ziet hoeven we bij het aanmaken van een List geen begingrootte mee te geven, wat we wel bij arrays moeten doen. Dit is een van de voordelen van List: ze groeien mee.

Elementen toevoegen

Via de Add() methode kan je elementen toevoegen aan de lijst. Je dient als parameter aan de methode mee te geven wat je aan de lijst wenst toe te voegen. Deze parameter moet uiteraard van het type zijn dat de List verwacht.

In volgende voorbeeld maken we een List aan die objecten van het type string mag bevatten en vervolgens plaatsen we er twee elementen in.

Elementen indexeren

Het leuke van een List is dat je deze ook kan gebruiken als een gewone array, waarbij je mbv de indexer elementen kan aanroepen. Stel bijvoorbeeld dat we een lijst hebben met minstens 4 strings in. Volgende code toont hoe we de string op positie 3 kunnen uitlezen en hoe we die op positie 2 overschrijven:

Ook de klassieke werking met for blijft gelden. De enige aanpassing is dat List<> niet met Length werkt maar met Count.

Wat kan een List nog?

Interessante methoden en properties voorts zijn:

  • Clear() :methode die de volledige lijst leegmaakt

  • Insert(): methode om element op specifieke plaats in lijst toe te voegen, bijvoorbeeld:

    voegt de string toe op de tweede plek en schuift de rest naar achter

  • Contains(): geef als parameter een specifiek object mee (van het type dat de List<> bevat) om te weten te komen of dat specifieke object in de List<> terug te vinden is. Indien ja dan zal true worden teruggeven.

  • IndexOf(): geeft de index terug van het element item in de rij. Indien deze niet in de lijst aanwezig is dan wordt -1 teruggegeven.

  • RemoveAt(): verwijder een element op de index die je als parameter meegeeft.

Foreach loops

Je kan met een eenvoudige for of while-loop over een lijst itereren, maar het gebruik van een foreach-loop is toch handiger.

Dit is dan ook de meestgebruikte operatie om eenvoudig en snel een bepaald stuk code toe te passen op ieder element van de lijst:

Kennisclip

NullReferenceException error in VS

Schrijf een statische klasse CSVDemo met een statische methode Run(). Deze downloadt automatisch en print de info in de eerste drie kolommen op het scherm. Tussen elke kolom verschijnt een tab.

(opname uit hoorcollege 4/3/20)

*

Opgelet: wanneer we "x heeft een y" zeggen gaat het niet over overerving, maar over .

Opgelet: Visual Studio gebruikt Expression Body Member syntax (herkenbaar aan de =>) om properties te overriden. Deze syntax kennen we niet (lees er gerust meer over ). Je schrijft dus best manueel de override van properties

Ook deze methode kan je dus overriden om twee objecten met elkaar te testen. Op het zal dieper in Equals ingaan worden om objecten te vergelijken, maar we tonen hier reeds een voorbeeld:

.

Bekijk volgende indien je dit wenst toe te passen.

Niet getreurd, je bent niet de enige. Overerving,System.object, Equals,...het is allemaal een hoop nieuwe kennis om te verwerken. Aan het gaan we dieper in bovenstaande materie in om een volledige Equals methode op te bouwen en we bij iedere stap uitgebreide uitleg geven.

JavaScript is a dynamically typed language, while c# is (usually) a statically typed language ()

Wanneer je de Visual Studio voor foreach gebruikt foreach [tab][tab] dan zal deze code ook een var gebruiken voor de iteration variabele. De compiler kan aan de te gebruiken array zien wat het type van een individueel element in de array moet zijn. De foreach van zonet kan dus herschreven worden naar:

dit bestand
Objecten als parameter of returnwaarde
Reference en value types
Stack vs heap
Stack vs heap
Gelieve 20 prijzen in te geven.
> 1
> 1
> 1
> 21
> 1
> 1
> 1
> 1
> 1
> 17
> 1
> 1
> 1
> 14
> 1
> 1
> 1
> 1
> 1
> 1
21.00
17.00
14.00
Het gemiddelde bedrag is 3.00.
9 Hearts
4 Clubs
... (er volgen nog 50 willekeurige combinaties)
Alle kaarten zijn getoond.
Wat wil je doen?
> 1
Joske Vermeulen, 21 jaar
Klas: EA2

Cijferrapport:
**********
Communicatie:             12
Programming Principles:   15
Web Technology:           13
Gemiddelde:               13.3

Mieke Vermeulen, 22 jaar
Klas: EB1

Cijferrapport:
**********
Communicatie:             10
Programming Principles:   16
Web Technology:           16
Gemiddelde:               13.3

Wat wil je doen?
Wat wil je doen?
> 1
Wat wil je doen?
> 2
Wat wil je doen?
> 1
Onbekend Onbekend, 18 jaar
Klas: EA1

Cijferrapport:
**********
Communicatie:             10
Programming Principles:   10
Web Technology:           10
Gemiddelde:               10.0

Wat wil je doen?
Wat wil je doen?
> 3
Wat is de indexpositie van de student die je wil aanpassen?
> 0
Wat wil je aanpassen?
1. Naam
2. Leeftijd
3. Klasgroep
4. Cijfer communicatie
5. Cijfer programmeren
6. Cijfer webtechnologie
> 4
Wat is de nieuwe waarde?
> 13
Wat wil je doen?
> 1
Onbekend Onbekend, 18 jaar
Klas: EA1

Cijferrapport:
**********
Communicatie:             13
Programming Principles:   10
Web Technology:           10
Gemiddelde:               11.0

Wat wil je doen?
Wat wil je doen?
> 4
Wat is de indexpositie van de te verwijderen student?
> 0
Wat wil je doen?
> 1
Mieke Vermeulen, 22 jaar
Klas: EB1

Cijferrapport:
**********
Communicatie:             10
Programming Principles:   16
Web Technology:           16
Gemiddelde:               13.3

Wat wil je doen?
class Soldier
{
   public Soldier() {Console.WriteLine("Soldier reporting in");}
}

class Medic:Soldier
{
   public Medic(){Console.WriteLine("Who needs healing?");}
}
Medic RexGregor= new Medic();
Soldier reporting in
Who needs healing?
class Soldier
{
   public Soldier(bool canShoot) {//...Do stuff  }
}

class Medic:Soldier
{
   public Medic(){Console.WriteLine("Who needs healing?");}
}
class Medic:Soldier
{
   public Medic(): base()
   {Console.WriteLine("Who needs healing?");}
}
class Soldier
{
   public Soldier(bool canShoot) {//...Do stuff  }
}

class Medic:Soldier
{
   public Medic():base(true)
    {Console.WriteLine("Who needs healing?");}
}
class Soldier
{
   public Soldier(bool canShoot) {//...Do stuff  }
}

class Medic:Soldier
{
   public Medic(bool canSh): base(canSh)
   {} 

   public Medic():base(true)  //Default
    {Console.WriteLine("Who needs healing?");}
}
class Restaurant
{
     protected int kosten=0;
     public virtual void PoetsAlles()
     {
           kosten+=1000;
     }
}

class Frituur:Restaurant
{
     public override void PoetsAlles()
     {
           kosten+= (1000 + 500);
     }

}
class Frituur:Restaurant
{
     public override void PoetsAlles()
     {
           base.PoetsAlles(); //eerste basiskost wordt opgeteld
           kosten+=500;  //kosten eigen aan frituur worden bijgeteld.
     }

}
class Paard: Dier
{
   public bool KanHinnikken{get;set;}
}

class Dier
{
   public void Eet()
   {
    //...
   }
}
Dier aDier= new Dier();
Paard bPaard= new Paard();
aDier.Eet();
bPaard.Eet();
bPaard.KanHinnikken=false;
aDier.KanHinnikken=false; //!!! zal niet werken!
class Paard: Dier
{
   public void MaakOuder()
   {
      leeftijd++; //  !!! dit zal error geven!
   }
}

class Dier
{
   private int leeftijd;
}
class Paard: Dier
{
   public void MaakOuder()
   {
      leeftijd++; //  werkt nu wel
   }
}

class Dier
{
   protected int leeftijd;
}
sealed class DoNotInheritMe
{
   //...
}
class ChildClass:DoNotInheritMe
{
   //...
}
class Vliegtuig
{
   public virtual void Vlieg()
   {
      Console.WriteLine("Het vliegtuig vliegt rustig door de wolken.");
   }
}

class Raket: Vliegtuig
{
}
Vliegtuig f1 = new Vliegtuig();
Raket spaceX1 = new Raket();
f1.Vlieg();
spaceX1.Vlieg();
Het vliegtuig vliegt rustig door de wolken.
Het vliegtuig vliegt rustig door de wolken.
class Raket:Vliegtuig
{
   public override void Vlieg()
   {
      Console.WriteLine("De raket verdwijnt in de ruimte.");
   }     
}
Vliegtuig f1= new Vliegtuig();
Raket spaceX1= new Raket();
f1.Vlieg();
spaceX1.Vlieg();
Het vliegtuig vliegt rustig door de wolken.
De raket verdwijnt in de ruimte.
    class Auto
    {
        virtual public int Fuel { get; set; }
    }
class LuxeAuto : Auto
{
   public bool HeeftVolleTank { get; set; }

   public override int Fuel
   {
      get { return base.Fuel; }
      set
      {
            if (value > 100)
            {
               HeeftVolleTank = true;
            }
            base.Fuel = value;
      }
   }
}

Methode

Beschrijving

Equals()

Gebruikt om te ontdekken of twee instanties gelijk zijn.

GetHashCode()

Geeft een unieke code (hash) terug van het object; nuttig om o.a. te sorteren.

GetType()

Geeft het type (of klasse) van het object terug.

ToString()

Geeft een string terug die het object voorstel.

Student stud1= new Student();
Console.WriteLine(stud1.GetType());
Student stud1= new Student();
Console.WriteLine(stud1.GetType().Name);
Console.WriteLine(stud1);
Console.WriteLine(stud1.ToString());
public virtual string ToString()
 { return GetType(); }
class Student
{
public int Leeftijd {get;set;}
public string Naam {get;set;}

public override string ToString()
{
   return $"Student genaamd {Naam} (Leeftijd:{Leeftijd})";
}
}
if(stud1.Equals(stud2))
   //...
stud1.Equals(stud2);
stud2.Equals(stud1);
//In de Student class
public override bool Equals(Object o)
{
     bool gelijk;
     if(GetType() != o.GetType()) 
         gelijk=false;
     else
     {
         Student temp= (Student)o; //Zie opmerking na code!
         if(Leeftijd== temp.Leeftijd && Naam== temp.Naam)
            gelijk=true;
         else gelijk=false;
      }
       return gelijk;
}
Wat wil je doen?
> 5
Gemiddelde cijfer op communicatie: 16

Wat wil je doen?
Klaar om te spelen.
De vorige kaart had waarde 7.
Hoger (0), lager (1) of gelijk (2)?
> 0
De vorige kaart had waarde 11.
Hoger (0), lager (1) of gelijk (2)?
> 1
De vorige kaart had waarde 2.
Hoger (0), lager (1) of gelijk (2)?
> 2
Spel voorbij!
double[] killdeathRates= {1.2, 0.89, 3.15, 0.1};
foreach (double kdrate in killdeathRates)
{
   Console.WriteLine($"Kill/Death rate is {kdrate}");
}
var getal= 5; //var zal int zijn
var myArray= new double[20]; //var zal double[] zijn
var tekst= "Hi there handsome"; //var zal string zijn
foreach (var kdrate in killdeathRates)
{
   Console.WriteLine($"Kill/Death rate is {kdrate}");
}
List<String> myStringList = new List<String>();
myStringList.Add("This is the first item in my list!");
myStringList.Add("And another one!");
Console.WriteLine(myStringList[3]);
myStringList[2] = "andere zin";`
for(int i = 0 ; i < myStringList.Count; i++)
{
    Console.WriteLine(myStringList[i])
}
myStringList.Insert(1,"A fourth sentence");
List<int> integerList=new List<int>();
integerList.Add(2);
integerList.Add(3);
integerList.Add(7);

foreach(int prime in integerList)
{
   Console.WriteLine(prime);
}
einde van deze cursus
einde van deze cursus

Oefeningen

Een bestaande klasse uitbreiden via overerving

Leerdoelen

  • overerving van bestaande klassen laten zien

  • toegang tot properties en methodes demonstreren

Functionele analyse

Maak een nieuwe klasse, WorkingStudent. Een werkstudent verschilt van een student omdat hij soms moet gaan werken en omdat hij een bepaald aantal werkuren per week moet presteren.

Technische analyse

Deze klasse erft over van de klasse Student die je voor het eerst hebt gebruikt in hoofdstuk 8.

Bovenop alle eigenschappen / methoden van Student heeft WorkingStudent:

  • een methode HasWorkToday() die willekeurig true of false teruggeeft. Er is geen methode NextBool in de klasse random, maar je kan een willekeurig getal tussen 0 en 1 genereren en de uitkomst vertalen in een boolean.

  • een property WorkHours die een aantal gepresteerde uren bijhoudt (als byte). Deze staat waarden tussen 1 en 20 toe. Lagere of hogere waarden worden automatisch aangepast (naar 1 of 20 naargelang of de waarde lager of hoger is).

    • de defaultwaarde van deze property is 10

Voeg een statische methode DemonstrateWorkingStudent() toe aan je klasse voor deze les. Deze methode doet volgende zaken:

  • ze maakt met de default constructor één gewone student aan (de properties mag je invullen zoals je zelf wil, maar vul ze wel in)

  • ze maakt met de default constructor één werkstudent aan (de properties mag je invullen zoals je zelf wil, maar vul ze wel in)

  • ze plaatst beide in een List<Student>

  • ze doorloopt met een foreach-lus de lijst en toont via Console.WriteLine de naam van elke student

  • je moet deze methode kunnen opstarten vanuit je keuzemenu voor dit onderwerp

Klassen met aangepaste constructor maken

Functionele analyse

We schrijven een applicatie die een lijst van af te handelen taken bijhoudt. Er zijn twee soorten taken: éénmalige taken en terugkerende taken. Elke taak heeft een beschrijving, maar terugkerende taken hebben ook een bepaalde hoeveelheid tijd waarna ze herhaald moeten worden.

Technische analyse

  • Maak een klasse Task met een property Description en een constructor die een waarde voor deze beschrijving verwacht (een string).

    • Wanneer deze constructor wordt opgeroepen, wordt volgende tekst getoond: "Taak is aangemaakt." Je vult hier zelf de beschrijving van de taak in.

  • Maak een subklasse RecurringTask van Task. Deze heeft een constructor die twee zaken verwacht: een beschrijving (nog steeds een string) en een aantal dagen (een byte).

    • Wanneer deze constructor wordt opgeroepen, wordt de tekst voor een gewone taak getoond. Daaronder wordt getoond: "Deze taak moet om de dagen herhaald worden."

  • Voeg een statische methode DemonstrateTasks() toe aan je klasse voor deze les. Deze methode maakt eerst een ArrayList van taken aan en herhaalt daarna volgende stappen tot de gebruiker aangeeft dat hij klaar is:

    • ze toont drie opties: een taak aanmaken, een terugkerende taak aanmaken of stoppen

    • indien de gebruiker vraagt een taak aan te maken, vraagt ze een beschrijving, maakt ze de taak en voegt ze deze toe aan de lijst

    • indien de gebruiker vraagt een terugkerende taak aan te maken, vraagt ze een beschrijving en een aantal dagen, maakt ze de terugkerende taak en voegt ze deze toe aan de lijst

    • indien de gebruiker wenst te stoppen, eindigt ze zonder resultaat

    • je moet deze methode kunnen opstarten vanuit je keuzemenu voor dit onderwerp

Voorbeeldinteractie

Wat wil je doen?
1. een taak maken
2. een terugkerende taak maken
3. stoppen
> 1
Beschrijving van de taak?
> TV ophangen
Taak TV ophangen is aangemaakt.
Wat wil je doen?
1. een taak maken
2. een terugkerende taak maken
3. stoppen
> 2
Beschrijving van de taak?
> Vuilzakken buiten zetten
Aantal dagen tussen herhaling?
> 7
Taak Vuilzakken buiten zetten is aangemaakt.
Deze taak moet om de 7 dagen herhaald worden.
Wat wil je doen?
1. een taak maken
2. een terugkerende taak maken
3. stoppen
> 3

Oefening: H12-ziekenhuis

Leerdoelen

  • werken met methodes

  • methodes overschrijfbaar maken

  • code specifieker maken met behulp van subklassen

Functionele analyse

Dit programma berekent de doktersrekening van een patiënt, op basis van een basisbedrag (€50) en een extra kost (€20/uur). In het geval van een verzekerde patiënt worden de kosten met 10% verlaagd.

Technische analyse

  • Schrijf twee klassen: Patient en InsuredPatient

  • Beide hebben als properties een naam (een string) en een verblijfsduur (een uint)

  • Beide hebben één constructor die de naam en verblijfsduur als parameter hebben

  • Beide hebben een methode ShowCost die een boodschap op het scherm print die zegt hoe veel die patiënt moet betalen

    • Omdat de kost anders bepaald wordt voor een gewone patiënt dan voor een verzekerde patiënt, moet je deze methode overschrijfbaar maken in Patient en moet je ze overschrijven in InsuredPatient. De kost wordt getoond met twee cijfers na de komma.

  • Voeg een statische methode DemonstratePatients() toe aan je klasse voor deze les. Deze methode maakt patiënten aan en roept hun methode ShowCost op, zodat onderstaande voorbeeldinteractie te zien is

voorbeeldinteractie

Vincent, een gewone patiënt die 12 uur in het ziekenhuis gelegen heeft, betaalt €290.00.
Tim, een verzekerde patiënt die 12 uur in het ziekenhuis gelegen heeft, betaalt €261.00.

Zie je geen €-symbool in je output? In het eerste semester hebben we gezien hoe je het gebruik van Unicode activeert.

Testscenario's

  • Test ook uit met een verblijf van 1 uur.

  • Test ook uit met een verblijf van 0 uur.

  • Test ook uit met de patiënten beschreven in de voorbeeldinteractie.

Oefening: dynamic dispatch

Leerdoelen

  • herbruik code ouderklasse

  • virtual, override, base

Functionele analyse

Onze rapporten van studenten missen belangrijke informatie. We willen te zien krijgen wie het statuut van werkstudent heeft.

Technische analyse

Pas je klassen Student en WorkingStudent aan, zodat de bestaande methode ShowOverview een uitgebreider overzicht toont voor werkstudenten (voor gewone studenten blijft het ongewijzigd). Dit heeft dan de vorm:

Joske Vermeulen, 21 jaar
Klas: EA2

Cijferrapport:
**********
Communicatie:             12
Programming Principles:   15
Web Technology:           13
Gemiddelde:               13.3
Statuut: werkstudent
Aantal werkuren per week: 10

De laatste twee regels komen bovenop de info die je eerder al toonde. Maak gebruik van base zodat je niet de volledige implementatie opnieuw hoeft te schrijven. Pas nu je methode DemonstrateWorkingStudent aan zodat niet de naam van elke student in de lijst wordt getoond, maar zodat zijn volledig rapport wordt getoond.

compositie
Overerving overzicht
hier
Virtual en override
Dit concept heet polymorfisme en wordt later uitgelegd
StackOverflow post
System.Object en ToString
Equals - objecten vergelijken
stackoverflow.com
code snippet
var keyword
De foreach loop
List<> klasse
Constructors bij overerving

Abstract

Abstract

Abstracte klassen

Soms maken we een parent-klasse waar op zich geen instanties van kunnen gemaakt worden: denk aan de parent-klasse Dier. Subklassen van Dier kunnen Paard, Wolf, etc zijn. Van Paard en Wolf is het logisch dat je instanties kan maken (echte paardjes en wolfjes) maar van 'een dier'? Hoe zou dat er uit zien.

Met behulp van het abstract kunnen we aangeven dat een klasse abstract is: je kan overerven van deze klasse, maar je kan er geen instanties van aanmaken.

We plaatsen abstract voor de klasse om dit aan te duiden.

Een voorbeeld:

abstract class Dier
{
  public int Name {get;set;}
}

Volgende lijn zal een error geven: Dier hetDier = new Dier();

We mogen echter wel klassen overerven van deze klasse en instanties van aanmaken:

class Paard: Dier
{
//...
}

class Wolf: Dier
{
 //..
}

En dan zal dit wel werken: Wolf wolfje= new Wolf();

Abstracte methoden

Het is logisch dat we mogelijk ook bepaalde zaken in de abstracte klasse als abstract kunnen aanduiden. Beeld je in dat je een Methode "MaakGeluid" hebt in je klasse Dier. Wat voor een geluid maakt 'een dier'? We kunnen dus ook geen implementatie (code) geven in de abstracte parent klasse.

Via abstracte methoden geven we dit aan: we hoeven enkel de methode signature te geven, met ervoor abstract:

abstract class  Dier
{
   public abstract string MaakGeluid();
}

Merk op dat er geen accolades na de signature komen.

Child-klassen zijn verplicht deze abstracte methoden te overriden.

De Paard-klasse wordt dan:

class Paard: Dier
{
  public override string MaakGeluid()
  { 
      return "Hinnikhinnik";
  }
}

(en idem voor de wolf-klasse uiteraard)

Abstracte methoden enkel in abstracte klassen

Van zodra een klasse een abstracte methode of property heeft dan ben je, logischerwijs, verplicht om de klasse ook abstract te maken.

Abstracte properties

Properties kunnen virtual gemaakt, en dus ook abstract. Volgende voorbeeld toont hoe dit werkt:

    abstract class Dier
    {
        abstract public int MaxLeeftijd { get;}
    }

    class Olifant : Dier
    {
        public override int MaxLeeftijd {
            get { return 100; }
        }
    }

Kennisclip

En als we polymorfisme gebruiken () dan mag dit ook: Dier paardje= new Paard();

(compilatie uit hoorcollege 18-19)

zie verder
Abstract klassen
Uitgewerkt voorbeeld Abstract en System.Object mbv Zoo-dieren
VS Installeren
VS Installeren
Licenicon
Licenicon
Licenicon