Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Voor de graduaatsopleiding hanteren we een vaste structuur voor de oefeningen.
Elke oefening begint met een unieke titel. Deze gebruik je voor de organisatie van je bestanden.
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.
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:
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:
We gebruiken de speciale syntax <EOF>
om aan te geven dat er een end-of-file signaal wordt gestuurd.
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.
Deze cursus wordt gebruikt als handboek binnen de graduaatsopleiding programmeren van de AP Hogeschool.
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
Deze cursus is gebaseerd op de overeenkomstige cursus van de bacheloropleiding toegepaste informatica.
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 CC-BY-NC 4.0 as shown here.
This is a human-readable summary of (and not a substitute for) the license.
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.
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.
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#:
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 ;)
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.
Ideale manier om programmeren meer in de vingers te krijgen op een speelse manier:
Pittige vragen van de jaarlijkse Vlaamse Programmeerwedstrijd:
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):
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:
Onderstaande richtlijnen zijn gebaseerd op . We beperken ons tot de zaken die we tijdens de cursus zien.
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!
using
directievenDeze komen alleen vooraan in het bestand voor en worden steeds gevolgd door de declaratie van de namespace.
In het eerste semester groeperen we al onze code in de namespace Programmeren
, in het tweede semester in de namespace ObjectgerichtProgrammeren
.
Elk zelf gedefinieerd datatype (klasse, struct, interface, enum type, later ook delegate) plaatsen we in een afzonderlijk bestand met dezelfde naam als dat datatype.
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.
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.
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
membersPlaats 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
.
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
.
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.
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.
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.
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:
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
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 overeenkomstige pagina in de cursus van de bachelor.
In alle lessen (hoorcollege en practica) hebben we 2 zaken nodig:
Deze cursus
Een laptop met daarop Visual Studio 2019 Community editie geïnstalleerd.
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.
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.
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:
Zodat je dus volgende code krijgt:
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.
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.
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 ( ; ).
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:
Kan je ook deze code uitvoeren?! Make it happen en ga dan gezwind naar het volgende hoofdstuk!
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:
Eender welke andere volgorde van bovenstaande algoritme zal vreemde (en soms fatale) fouten geven.
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).
Deze cursus werd geschreven in Markdown. 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 docs.microsoft.com.
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.
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:
Vanaf dan zal alle tekst die je na deze 2 expressies via WriteLine
naar het scherm stuurt met deze kleuren werken.
Een voorbeeld:
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.
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:
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
We nemen terug ons eerste programma erbij en gaan hier aan verder werken:
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).
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)
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:
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:
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:
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:
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:
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.
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.
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.
De verschillende datatypes bespreken we in een volgend hoofdstuk.
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 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
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 deze pagina met richtlijnen).
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.
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:
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).
We kunnen een stuk tekst als commentaar aangeven door voor de tekst /*
te plaatsen en */
achteraan. Een voorbeeld:
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.
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.
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 Visual Studio Code 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 visualstudio.microsoft.com/vs.
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.
Na het opstarten van VS krijg je het startvenster te zien van waaruit je verschillende dingen kan doen.
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.
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.
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".
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.
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): hier.
Als AP-student heb je ook recht op een Visual Studio Enterprise licentie. Wil je deze gebruiken, download deze dan via https://visualstudio.microsoft.com/vs/ en zorg ervoor dat je een key aanvraagt via de tab 'Software' op Azure for education. Je kan deze sleutel vervolgens via Help->Register in VS invoeren.
Je code kan niet gecompileerd en uitgevoerd worden zolang er fouten in je code staan (dit bespraken we reeds in een eerder hoofdstuk).
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.
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.
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.
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.
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).
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):
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.
Voor de gehele getallen:
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.
Voor de kommagetallen zijn er maar 3 mogelijkeden. Ieder datatype heeft een 'voordeel' tegenover de 2 andere, dit voordeel staat vet in de tabel:
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
.
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.
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.
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.
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.
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'.
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.
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:
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)
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.
de .NET desktop development en .NET Core cross-platform development workload selecteert als te installeren tools.
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 .
Alle format specifiers staan .
Wens je meer informatie over string.Format
, kijk dan .
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
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
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.
Over expressies in C# is véél te vertellen, maar niet aan de orde in deze cursus. Bekijk zeker volgende tekst indien je meer interesse hebt.
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:
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:
Een ander voorbeeld van een expressie-resultaat toewijzen maar nu met literals (stel dat we temperatuursVerschil reeds hebben gedeclareerd eerder):
Uiteraard mag je ook combinaties van literals en variabelen gebruiken in je expressies:
Operators in C# zijn de welgekende 'wiskundige bewerkingen' zoals optellen (+
), aftrekken (-
), vermenigvuldigen (*
) en delen (/
). Deze volgen de wiskundige regels van volgorde van berekeningen:
Haakjes
Vermenigvuldigen, delen en modulo: *
(vermenigvuldigen), /
(delen) en %
(rest na deling, ook module genoemd)
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:
Je kan nu complexe berekeningen doen door literals, operators en variabelen samen te voegen. Bijvoorbeeld om te weten hoeveel je op Mars zou wegen:
%
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:
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
:
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.
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.
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:
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:
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:
Volgende niet:
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:
En nu krijgen we wel 4.5
.
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:
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:
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.
gebruik van string interpolation
Oefening H1-maaltafels en H1-ruimte dienen we te herschrijven volgens de principes van string interpolation.
console applicatie
Zie oefening H1-maaltafels en H1-ruimte.
Pas string interpolatie mbv $
(manier 2) toe.
Zie oefening H1-maaltafels en H1-ruimte.
Hou het voorlopig op de cursus.
gebruik van string interpolation
gebruik van Environment
class
Maak een applicatie die de belangrijkste computer-informatie (geheugen, etc) aan de gebruiker toont.
console applicatie
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.
wat gebeurt er wanneer je het datatype string zou wijzigen in int?
Hou het voorlopig op de cursus.
gebruik van math class
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.
(Bron afbeelding: https://www.esdsite.nl)
console applicatie
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.
wat gebeurt er wanneer je een hoger getal dan 9 zou invoeren?
Hou het voorlopig op de cursus.
gebruik van UNICODE
Zie deel 1.
Zie deel 1 en plaats het geheel in een mooie UNICODE-tabel.
Hier enkele nuttige tekens:
of:
wat gebeurt er wanneer je een waarde van circle 1, 2 of 3 uit meer dan twee cijfers bestaat?
Hou het voorlopig op de cursus.
gebruik van Process.Start()
verwerken van uitvoer
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.
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:
Probeer van bovenstaande programma's al die, die compatibel zijn met je besturingssysteem.
Hou het voorlopig op de cursus.
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".
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:
Het datatype (bv int
, double
).
Een identifier zodat de variabele uniek kan geïdentificeerd worden (volgens de naamgevingsregel van C#).
(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:
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:
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:
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:
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
.
gebruik van variabelen om input en output via Console.ReadLine
en Console.WriteLine
op te slaan en terug te tonen
Een applicatie vraagt je tekst in te voeren die dan daarna zal worden getoond met allemaal hoofdletters.
console applicatie
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.
Voer tekst in met spaties
Voer tekst in van meer dan 100 karakters
Voer tekst in van 1 karakter
Voer geen tekst in
Hou het voorlopig op de cursus.
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
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.
console applicatie
Werking
De vraag wordt gesteld om een getal in te typen en daarna op enter/return te drukken.
Er wordt gevraagd een tweede getal in te typen en dan op enter/return te drukken.
De twee getallen worden opgeteld.
Het resultaat wordt weergegeven.
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);
Voer tekst in.
Voer een getal met 100 cijfers in.
Voer geen getal in.
Hou het voorlopig op de cursus.
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
Een applicatie zal voor jou het gemiddelde verbruik van een wagen berekenen.
Hiervoor worden volgende vragen gesteld:
Hoeveel liter is er nog aanwezig in de benzinetank.
Hoeveel liter zit er nog in de benzinetank na de rit.
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.
console applicatie
Werking
De vraag wordt gesteld om het aantal liter, aanwezig in de benzinetank, op te geven.
Daarna wordt gevraagd om ook het aantal liter op te geven na de rit.
De kilometerstand van de aanvang van de rit wordt gevraagd.
Uiteindelijk ook de kilometerstand na het beëindigen van de rit wordt opgevraagd.
De screenshot vermeldt het afgeronde verbruik, maar dat hoef je niet te tonen. De instructie om af te ronden heb je nog niet gezien.
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.
Voer tekst in.
Voer een getal met 100 cijfers in.
Voer geen getal in.
Hou het voorlopig op de cursus.
expressies schrijven
voorrang van operatoren
effect van operaties naargelang datatype begrijpen
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
console applicatie
Eerst wordt een resultaat berekend, daarna wordt het geprint.
Test uit met getallen van het type int
.
Test uit met getallen van het type float
.
Hier vind je een tabel terug die uitlegt welke operaties voorrang hebben.
expressies schrijven
voorrang van operatoren
effect van operaties naargelang datatype begrijpen
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.
console applicatie
Eerst wordt het resultaat berekend, daarna wordt het geprint.
Test uit met getallen van het type int
.
Test uit met getallen van het type float
.
Hou het voorlopig op de cursus.
de console leegmaken
werken met wiskundige operatoren
interactie met de gebruiker
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".
console applicatie
(enzovoort)
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.
Test uit zoals gegeven.
Test uit voor 511. Je zou maar één teken in je code moeten aanpassen als je de instructies hebt gevolgd.
Hou het voorlopig op de cursus.
werken met kommagetallen
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
console applicatie
Plaats je gewicht in een variabele. Kies zelf een geschikt type.
Test uit voor je eigen gewicht.
Test uit voor het gewicht van een persoon met een massa van 100kg.
Hou het voorlopig op de cursus.
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.
Zonder een uitleg te geven over het verschil tussen ASCII en Unicode is het vooral belangrijk te weten dat je best met Unicode werkt.
Plaats bovenaan je Main: Console.OutputEncoding = System.Text.Encoding.UTF8;
Je kan nu op 2 manieren dit teken in console plaatsen
Kopieer het karakter zelf en plaats het in je code waar je het nodig hebt, bijvoorbeeld:
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:
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:
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:
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.
Neem volgende korte demonstratie van debugging door: .
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!
De Random
-klasse laat je toe om eenvoudig willekeurige gehele en komma-getallen te maken. Je moet hiervoor twee zaken doen:
Maak eenmalig een Random-generator aan: Random randomgen= new Random();
(wat dit juist wil zeggen zien we in ).
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:
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:
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:
Ik krijg dezelfde random getallen? Wat nu?
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 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:
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
).
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:
Plaats je cursor op Pow
.
Druk op F1
op je toetsenbord.
Je krijgt nu de help-files te zien van deze methode op MDSDN.
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:
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.
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:
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
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:
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.
Gebruik $-string interpolatie om de informatie in de tabel te tonen zodat je volgende uitvoer kunt genereren:
Zoek het teken(s) dat je nodig hebt in een Unicode-tabel ()
Schrijf de Methode zonder argumenten. Bijvoorbeeld Math.Pow()
(je mag de rode error negeren).
En wat als je toch foute invoer wilt opvangen? Dan is TryParse
je vriend. We zullen dit bespreken wanneer we aan Methoden komen. Ongeduldig? .
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.
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!
gebruik van de Math
namespace
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.
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 hier (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.
console applicatie
gebruik van de Math
namespace
je programma's delen met anderen
kommagetallen parsen
stringinterpolatie en formattering
Maak een programma dat aan de gebruiker z'n lengte en gewicht vraagt en vervolgens z'n berekende BMI (Body Mass Index) toont.
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.
console applicatie
Bekijk de volgende kennisclip en geef je uitvoerbaar bestand van het programma aan een klasgenoot. Kijk of hij het kan uitvoeren.
tussenresultaten bijhouden
werken met de Math
namespace
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.
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.
console applicatie
tussenresultaten bijhouden
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
.
Je gebruikt best een variabele om de voorlopig som bij te houden. Zo moet je minder code herhalen.
console applicatie
werken met willekeurige getallen
Maak een orakel/waarzegger, namelijk de kleine broer of zus van het Orakel van Delphi. Het programma zal aan de gebruiker vertellen hoe lang deze nog zal leven. Bijvoorbeeld: "Je zal nog 15 jaar leven.".
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.
Je moet hier een willekeurig getal bepalen en dat dan in een mooie vorm presenteren aan de gebruiker. Gebruik hiervoor de klasse Random
.
console applicatie
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:
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!
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
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).
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 (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 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
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:
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.
Ook de beslissingsstructuren worden leesbaarder:
of een switch:
Intern worden de enum-variabelen als ints bewaard. In het geval van de Weekdagen
zal maandag de waarde 0 krijgen, dinsdag 1, etc.
Volgende conversies met behulp van casting zijn dan ook perfect toegelaten:
Wil je dus bijvoorbeeld 1 dag bijtellen dan kan je schrijven:
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.
Je kan perfect leven zonder enum
. Vele programmeurs voor je hebben dit bewezen. Echter, van zodra ze enum
ontdekten (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:
In dit deel zullen we bekijken hoe we ons programma dynamischer kunnen maken met behulp van het if-statement.
De if
is een van de elementairste constructies in een programmeertaal. De syntax is als volgt:
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:
De uitvoer van dit programma zal zijn:
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:
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:
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!
Er zijn enkele veelgemaakte fouten waar je op moet letten:
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 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
:
Merk ook op dat je code anders uitlijnen géén invloed heeft op de uitvoer (wat bijvoorbeeld wel zo is bij de programmeertaal Python).
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
:
Met de relationele operatoren (==
, !=
, <
, >
, <=
en >=
) kunnen we complexere expressies schrijven die als uitkomst waar (true
) of niet waar (false
) geven.
Een voorbeeld:
Uitvoer van bovenstaande code zal zijn:
We kunnen ook meerdere expressies combineren zodat we complexere uitdrukkingen kunnen maken. Hierbij kan je gebruik maken van de logische operatoren (&&
, ||
, !
) .
Een voorbeeld:
Uitvoer van dit programma zal zijn:
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:
Kan je zelf een flowchart van bovenstaande code tekenen? Try it!
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:
Merk op dat else if
niet meer is dan een verkorte schrijfwijze voor nesting van een if
in een else
-block.
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):
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:
De logische EN, OF en NIET-operatoren die je kent van de booleaanse algebra kan je ook gebruiken in C#:
Je kan de niet-operator voor een expressie zetten om het resultaat hiervan om te draaien. Bijvoorbeeld:
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.
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!
C#-syntax
Betekenis
>
groter dan
<
kleiner dan
==
gelijk aan
!=
niet gelijk aan
<=
kleiner dan of gelijk aan
>=
groter dan of gelijk aan
C#-syntax
Betekenis
&&
en-operator
||
of-operator
!
niet-operator
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.
Wil je dus getal ook nog buiten de if
gebruiken zal je je code moeten herschrijven zodat getal
VOOR de if
wordt aangemaakt:
De scope van variabelen is soms wat verwarrend maar wel een onderdeel dat je deze hele cursus zal zien terugkomen.
Hopelijk kan volgende kennisclip je helpen: Kennisclip "Scope van variabelen.
Zolang je in de scope van een variabele bent kan je geen nieuwe variabele met dezelfde naam aanmaken:
Volgende code is dus 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:
De syntax van een while loop is eenvoudig:
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:
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!
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:
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.
Begrijp je wat dit programma zal doen?
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":
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:
Een bug die een oneindige loop veroorzaakt:
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.
Let er op dat de scope 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:
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:
Dankzij loops kunnen we nu ook eenvoudiger omgaan met foutieve input van de gebruiker. Stel dat we volgende vraag hebben:
Met een loop kunnen we nu deze vragen blijven stellen tot de gebruiker een geldige input geeft:
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.
een eigen programma kunnen uitvoeren
input en output via Console.ReadLine
en Console.WriteLine
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]”.
console applicatie
Wat het lezen en schrijven van tekst betreft moet gebruik gemaakt worden Console.WriteLine
en Console.ReadLine
.
Probeer meer dan 200 tekens in te voeren
Probeer geen tekst in te voeren
Hou het voorlopig op de cursus.
een eigen programma kunnen uitvoeren
input en output via Console.ReadLine
en Console.WriteLine
herhaling van de leerdoelen uit H0-eerste-programma
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.
console applicatie
Wat het lezen en schrijven van tekst betreft moet gebruik gemaakt worden Console.WriteLinea
en Console.ReadLine
.
Probeer meer dan 200 tekens in te voeren
Probeer geen tekst in te voeren
Hou het voorlopig op de cursus.
een eigen programma kunnen uitvoeren
input en output via Console.ReadLine
en Console.WriteLine
de computer leren zien als "domme verwerker"
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.
console applicatie
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.
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.
Hou het voorlopig op de cursus.
de kleur van tekst in de console aanpassen
herhaling van de leerdoelen uit H0-rommelzin
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:
DarkGreen
DarkRed
DarkYellow
Blue
Cyan
Red
console applicatie
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.
Test opnieuw uit met een kleur, maaltijd, auto, film en boek naar keuze.
Hou het voorlopig op de cursus.
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.
We tonen nu kort de verschillende program flow elementen en hoe ze in een flowchart voorkomen.
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
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#.
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.
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 .
Indien niet expliciet vermeld mag je kiezen met wat voor loop (for, while, do while) je het probleem zal oplossen.
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)
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 +=
).
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.
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.
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!
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.
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:
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:
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
:
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.)
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:
In dit geval zullen zowel de waarden 2
en 3
resulteren in de zin "Laden of opslaan gekozen" op het scherm.
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:
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 ).
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:
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:
In de eerste toekenning proberen we dus een literal van het type string toe te kennen een variabele van het type char.
In de tweede toekenning proberen we een literal van het type char toe te kennen een variabele van het type string.
In de laatste toekenning proberen we een literal van het type char toe te kennen aan een variabele van het type int.
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:
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.
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.".
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:
of
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:
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:
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") naar20
en dan aanvar2
toegewezen dat enkelint
aanvaardt.
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:
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):
Nu zal temperatuurGemiddeld wel de waarde 22.5 bevatten.
Casting is echter niet nodig als je aan widening doet (een kleiner type in een groter type steken), als volgt:
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
.
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:
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 naarTrue
geconverteerd worden. Enkel indien je0
, (int
) of0.0
ingeeft, dan krijg jeFalse
. In quasi alle andere gevallen krijg je altijdTrue
.
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.
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
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:
Via de kan je heel eenvoudig een flowchart genereren van pseudocode.
Maak volgende opdracht van :
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 ().
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.
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.
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:
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.
conditionele boodschappen
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.
Via if
en else
(en dus ook else if
) kan je gevallen onderscheiden. Gebruik ConsoleColor.Red
, ConsoleColor.Green
, ConsoleColor.Yellow
en ConsoleColor.Magenta
.
console applicatie
De tekst zou in het groen moeten verschijnen maar Gitbook staat dit niet meteen toe.
conditionele berekeningen
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.
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
.
console applicatie
(Na de uitbreiding)
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.
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.
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.
console applicatie
conditionele berekeningen
geneste condities
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.
gebruik de modulo-operator (%
) om deelbaarheid door 4 na te gaan
gebruik een constructie met geneste if
s (en else
s) om alle gevallen af te handelen
console applicatie
conditionele berekeningen
werken met switch
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.
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
!
console applicatie
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.
conditionele berekeningen
werken met switch
werken met enumeraties
Vul de oefening aan uit het vorige hoofdstuk (zie deze pagina). 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.
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.
console applicatie
Dit is een complexere oefening dan de vorige! Pak het stapje voor stapje aan en gebruik indien nodig de debugger.
werken met enumeraties
conversie van enums van en naar getallen
werken met switch
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.
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
console applicatie
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.
gebruik van string interpolation
gebruik van System.IO
Je moet informatie over de harde schijven op je systeem weergeven.
console applicatie
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:
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:
wat gebeurt er wanneer je het datatype int zou wijzigen in string?
Hou het voorlopig op de cursus.
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)
Een extra grote hoop oefeningen om je loops te drillen (originele bron). De oefeningen zijn gerangschikt naar moeilijkheid, je moet deze allemaal met loops oplossen! Hoe ver geraak je?
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.
Toon alle natuurlijke getallen van 1 tot n.
Toon alle natuurlijke getallen van n tot 1.
Toon alle even getallen tussen 1 en 100.
Toon alle oneven getallen tussen 1 en 100.
Toon de som van alle getallen van 1 tot n (dus 1+2+3+4+...+n).
Toon de som van alle even getallen van 1 tot n.
Toon de som van alle oneven getallen van 1 tot n.
Schrijf een programma dat het aantal digits in een getal telt (het getal 12348 heeft bijvoorbeeld 5 digits).
(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.
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)
Toon het alfabet van a tot z.
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)).
Schrijf een programma een getal n ontbindt in factoren. 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 Fibonacci tot n termen.
lussen gebruiken om een resultaat op te bouwen
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.
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.)
console applicatie
geneste lussen gebruiken om een meerdere resultaten te laten zien
Je krijgt een getal van de gebruiker. Je moet alle priemgetallen kleiner of gelijk aan dit getal laten zien.
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.
console applicatie
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.
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):
Vervolgens kan je deze methode elders oproepen als volgt, indien de methode geen parameters vereist:
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.
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:
Mogelijke manieren om deze methode in je programma te gebruiken zouden kunnen zijn:
Of bijvoorbeeld ook:
Hier een voorbeeld van een methode die de faculteit van 5 berekent. De oproep van de methode gebeurt vanuit de Main-methode:
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:
Parameters kunnen op 2 manieren worden doorgegeven aan een methode:
Wanneer een parameter by value wordt meegegeven aan een methode, dan wordt een kopie gemaakt van de huidige waarde die wordt meegegeven.
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.
Je methode definitie kan ook 1 of meerdere parameters bevatten. Hierbij gebruik je volgende syntax:
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:
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:
Dit zal als resultaat geven
Merk dus op dat dankzij je methode, je véél code maar één keer moet schrijven, wat de kans op fouten verlaagt.
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:
Stel dat we nu in onze main volgende aanroep doen:
Dit zal een ander resultaat geven dan wanneer we volgende code zouden uitvoeren:
Ook de volgorde is belangrijk zeker wanneer je met verschillende types als parameters werkt:
Deze aanroep is correct:
Deze is FOUT en zal niet compileren:
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:
Wanneer we nu elders de methode Macht
gebruiken dan krijgen we automatische extra informatie:
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:
(^ 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:
Dit geeft als uitvoer:
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.
We kunnen nu terug naar onze rente-berekenaar en dit programma aanzienlijk vereenvoudigen door gebruik te maken van methoden. Namelijk als volgt:
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);}
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:
De aanroep van deze methode in de main wordt dan de volgende:
Vaak moet je code schrijven van volgende vorm:
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:
De code van zonet kan je dan nu herschrijven naar:
een taak herhaaldelijk uitvoeren met een lus
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
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.
console applicatie
(Dit programma kan blijven verder lopen zo lang je wil.)
een taak herhaaldelijk uitvoeren met een lus
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.
Genereer een willekeurig getal tussen 1 en 3 om de computer te laten kiezen.
Teken een flowchart!
console applicatie
(Helemaal op het einde)
of
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)
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).
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
.
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.
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 ).
Stel dat je de dagelijkse neerslag wenst te bewaren. Dit kan je zonder arrays eenvoudig:
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.
Grote delen van dit hoofdstuk zijn vertaald uit het handboek C# 4.0 Essentials.
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.
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 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:
Stel dat je dus een array van strings wenst waarin je verschillende kleuren zal plaatsen dan schrijf je:
Vervolgens kunnen we later waarden toekennen aan de array, hiervoor gebruiken we het new
sleutelwoord.
Je array zal vanaf dit punt een lengte van 5 hebben en kan niet meer groeien.
Indien je direct waarden wilt toekennen (initialiseren) tijdens het aanmaken van de array zelf dan mag dit ook als volgt:
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).
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:
De 3 manieren om arrays te declareren zijn dus:
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:
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.
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.
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:
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:
Stel dat we een array aanmaken (eerste lijn) dan kunnen we dus bijvoorbeeld het getal 90
op het scherm tonen als volgt:
of nog korter:
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:
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:
Nog beter is het natuurlijk deze code (die 4 keer quasi dezelfde statement bevat) te vereenvoudigen tot:
Of het equivalent met een for-loop:
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:
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:
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:
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.
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[]
).
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:
Dan is dat hetzelfde als schrijven:
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:
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.
"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:
Maak een methode FilmRuntime() die 3 parameters accepteert:
Een string die de naam van de film bevat
Een integer die duur in minuten van de film bevat
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.
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.
Zorg ervoor dat de opwarmers uit Deel 0 oefeningen hiervoor steeds minstens 1 optionele parameter hebben. Roep deze methoden aan via named parameters.
Kan je code uit vorige hoofdstukken herbruiken door deze in handige methoden te plaatsen zodat je code leesbaarder én bruikbaarder wordt?
Kan je deze code leesbaarder maken door methoden (en loops) toe te voegen?
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?
Volgende sectie is grotendeels gebaseerd op het volgende artikel.
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:
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:
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 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:
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:
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
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:
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.
methodes
Schrijf een hele reeks methodes die je samen test:
Methode Square
die het kwadraat van een ingevoerd getal berekent.
Methode Radius
die de straal van een cirkel kan berekenen waarvan je de diameter meegeeft.
Methodes Circumference
en Surface
(in de formule gebruik je Math.PI
).
Methode Largest
die het grootste van 2 getallen teruggeeft.
Methode IsEven
die bepaalt of een getal even of oneven is (geeft een bool
terug die true
is indien even).
Methode ShowOdd
die alle oneven getallen van 1 tot n toont waarbij n als parameter wordt meegegeven.
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.
console applicatie
methodes
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".
Je persoonlijke informatie mag hardcoded in je methode staan. Bedoeling is dat je de methode kan aanroepen als volgt:
Deze methode voert een taak uit, maar geeft geen antwoord dat je verder zou kunnen gebruiken. Het return type is dan ook void
.
console applicatie
methodes-met-parameters
Maak een flexibelere versie van H6-voorstellen, die je persoonlijke gegevens als argumenten meekrijgt.
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:
console applicatie
methodes
Schrijf een methode die drie int
s aanvaardt en de grootste als resultaat teruggeeft.
Aangezien het maar om 3 argumenten gaat, kan je dit oplossen met if
s die het eerste en het tweede argument vergelijken, het eerste en het derde argument,... Test je methode uit in een voorbeeldprogrammaatje.
console applicatie
methodes
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.
Maak gebruik van Random
en een for
-lus. Een Random
genereert normaal alleen getallen, maar via casting kan je die getallen omzetten in char
s. 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.
console applicatie
methodes met default argumenten
Maak een methode FilmRuntime
met 3 parameters:
Een string die de naam van de film bevat
Een integer die duur in minuten van de film bevat
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:
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.
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.
console applicatie
werken met verschillende methodes
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.
Schrijf je vier methoden, telkens met twee parameters.
Roep één van deze vier methoden aan a.h.v. een switch constructie binnen uw main.
console applicatie
We zullen nu enkele basisconcepten van klassen en objecten toelichten aan de hand van praktische voorbeelden.
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:
Het keyword static
komt bij een klasse niet aan te pas (of toch minder zoals we later zullen zien)
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.
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
.
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:
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!
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.
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.
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:
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:
Met een for-loop kunnen we deze veel voorkomende code-constructie verkort schrijven.
De syntax van een for
-loop is de volgende:
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:
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.
Lees zeker deze for tutorial na want er zijn nog enkele subtiliteiten in for-loops die we hier niet behandelen.
Als je in Visual Studio for
typt en dan tweemaal op [tab] duwt krijg je kant en klare for-loop code.
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."
Bron StackOverFlow: Are break
and continue
bad programming practices?
Lees meer over het gebruik van break
hier.
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;
:
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":
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:
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:
Wanneer je de Sort-methode toepast op een array van strings dan zullen de elementen alfabetisch gerangschikt worden.
Met de Array.Reverse()
-methode kunnen we dan weer de elementen van de array omkeren (dus het laatste element vooraan zetten en zo verder:
Een array volledig leegmaken (alle elementen op ‘null’ zetten) doe je met de Array.Clear
-methode, als volgt:
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:
Volgend voorbeeld toont het gebruik van deze methode:
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:
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:
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.
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:
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
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:
We vragen nu aan de gebruiker van welk product de prijs getoond moet worden:
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:
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:
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:
De uitvoer zal worden:Ik ben Tim
.
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:
De uitvoer van deze code zal zijn: haxlo
.
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.
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. .
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.
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).
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.
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:
In dit hoofdstuk bespreken we eerst waarom properties nodig zijn. Vervolgens bespreken we de 2 soorten properties die er bestaan:
Full properties
Auto 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:
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!
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.
Je zou dan kunnen doen:
Een full property ziet er als volgt uit:
Dankzij deze code kunnen we nu elders dit doen:
Vergelijk dit met de vorige alinea waar we dit met Get en Set methoden moesten doen. Deze property syntax is veel eenvoudiger in het gebruik.
We zullen de property nu stuk per stuk analyseren:
public 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 energy
vtoch 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!
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:
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.
We zijn niet verplicht om zowel de get
en de set
code van een property te schrijven.
Write-only property
We kunnen dus enkel energy
een waarde geven, maar niet van buitenuit uitlezen.
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:
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:
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:
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
We willen echter ook soms de volledige naam op het scherm tonen ("Voornaam + Achternaam"). Via een read-only property kan dit supereenvoudig:
Of nog eentje:
Nog een voorbeeldje:
Vaak gebruiken we dit soort read-only properties om data te transformeren. Stel bijvoorbeeld dat we volgende klasse hebben:
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:
De herschreven klasse met autoproperties (autoprops):
Beide klassen hebben exact dezelfde functionaliteit, echter de klasse aan de rechterzijde is aanzienlijk eenvoudig om te lezen en te typen.
Je mag autoproperties beginwaarden geven door de waarde achter de property te geven, als volgt:
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:
Voorgaande property kan dus NIET herschreven worden met een automatic property.
Je kan automatic properties ook gebruiken om bijvoorbeeld een read-only property te definiëren . Als volgt:
Originele klasse:
Met autoprops:
En andere manier die ook kan is als volgt:
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.
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
Een aantal oefeningen is geïnspireerd op oefeningen van .
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:
maar wel: true, false, true, ...
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.
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.
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.
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.
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.
"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?
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 ^
).
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:
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 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 )
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:
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:
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:
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.
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 .
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:
Schrijf een methode VermenigvuldigMatrix die 2 matrices als invoer verwacht en als resultaat een nieuwe matrix teruggeeft die het product van beide matrices bevat.
" 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()
).
Voorbeeld output:
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)
Steve Jobs, de oprichter van Apple, was een fervent fan van OOP. In een interview, way back, gaf hij volgende uitstekende uitleg:
De enige manier om FirstName een waarde te geven is via de constructor van de klasse. Alle andere manieren zal een error genereren.
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 .NET klasse DateTime
is de ideale manier om te leren werken met objecten. Het is een nuttige en toegankelijk klasse.
Grote delen van deze tekst komen van zetoode.com.
Er zijn 2 manieren om DateTime
objecten aan te maken:
Door aan de klasse de huidige datum en tijd te vragen via DateTime.Now
Door manueel de datum en tijd in te stellen via de constructor
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:
Via de constructor kunnen we beginwaarden meegeven bij het maken van een nieuw object. Er zijn 11 manieren waarop dit kan zoals je hier kan zien.
Enkele voorbeelden:
Ieder DateTime
object dat je aanmaakt heeft en hoop nuttige methoden.
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:
(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:
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
Alle properties van DateTime zijn read-only.
Een voorbeeld:
Uiteraard mag je ook deze properties gebruiken om direct naar het scherm te schrijven:
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:
Wil je nog meer controle over de output dan kan je ook zelf je formaat specifieren.
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:
Een lijst van alle cultures in .NET kan je hier terugvinden.
Opgelet, enkel indien een specifieke culture op je computer staat geïnstalleerd zal je deze kunnen gebruiken.
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.
Parsen laat toe dat je strings omzet naar DateTime
. Dit is handig als je bijvoorbeeld de gebruiker via ReadLine
tijd en datum wilt laten invoeren:
Zoals je ziet roepen we Parse
aan op DateTime
en dus niet op een specifiek object.
Deze nuttige methode geeft een bool
terug om aan te geven het meegegeven object eens schrikkeljaar is of niet:
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:
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.
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.
Pokemon
(h9-pokeattack)herhalen aanmaken klassen
herhalen aanmaken instantiemethoden
herhalen aanmaken full properties
We willen een bootleg Pokémon spel maken. We starten met een klasse om Pokémon voor te stellen.
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.
(In groen, rood, blauw en geel.)
arrays van objecten
null
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.
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.
arrays van objecten
null
Je moet ook het geval afhandelen waarbij al je Pokémon KO zijn.
Breid je methode TestConsciousPokemon
uit zodat ze niet crasht wanneer al je Pokémon KO zijn. Doe dit in een nieuwe versie, TestConsciousPokemonSafe
.
call by value vs. call by reference
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.
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.
gebruik van Random
null
guard
call by reference
Hoe wreed het ook is, Pokémon zijn bestemd om tegen elkaar te vechten. Schrijf een simulatie van een gevecht met een willekeurig element.
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
Ga na dat al je code van de eerste oefeningen nog werkt nadat je de laatste hebt afgerond en plaats alles op Bitbucket.
Code makkelijk toegankelijk maken
Vermijden heleboel piepkleine projectjes aan te maken
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.
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.
Doorloop de instructies en uitleg op ModernWays, maar let op volgende zaken:
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.
Kennismaking met OOP
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.
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
Kennismaking met OOP
Kennismaking met refactoring
Toepassing van encapsulatie
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.
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:
Kennismaking met OOP Kennismaking met refactoring
Toepassing van abstractie
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.
Je krijgt volgende twee files. De bestandsnamen volgen de afspraak:
en
Volg hierbij volgende stappen:
Maak de random generator statisch onderdeel van de klasse BarkingDog
.
Voeg volgende code toe binnen de klasse BarkingDog
:
git status -u
!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
.
Main
methodesIndien 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.)
aanmaken van DateTime
objecten
formatteren van DateTime
objecten
We willen voor een willekeurige datum kunnen bepalen welke dag van de week het is.
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
aanmaken van DateTime
objecten
We willen weten hoe veel fracties van een seconde al verlopen zijn sinds het begin van de jaren 2000.
.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
gebruik van een statische property
We willen bepalen hoe veel schrikkeljaren er zijn tussen 1800 en 2020.
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
eenvoudig code leren timen
gebruiken van DateTime
herhaling arrays
We zijn benieuwd hoe lang het duurt een array van 1 miljoen int
s te maken en op te vullen met de waarden 1,2,...
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
werken met klassen en objecten
gebruik maken van simpele properties en methodes
Dit programma geeft op basis van de input van een percentage de graad weer die je met dit gegeven zou behaald hebben.
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.
werken met klassen en objecten
gebruik maken van simpele properties en methodes
Dit programma geeft op basis van de input van een percentage de graad weer die je met dit gegeven zou behaald hebben.
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
.
werken met klassen en objecten
gebruik maken van properties
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.
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:
werken met klassen en objecten
gebruik maken van properties om geldige waarden af te dwingen
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.
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
.
(Er worden twee rechthoeken en twee driehoeken aangemaakt. De afmetingen van de eerste rechthoek worden eerst op -1
en 0
ingesteld.
werken met klassen en objecten
gebruik maken van properties om geldige waarden af te dwingen
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.
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 byte
s 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
.
werken met klassen en objecten
gebruik maken van properties om geldige waarden af te dwingen
Deze is gelijkaardig aan de vorige versie van het programma, maar gebruikt iets geavanceerdere technieken.
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
.
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:
Bij variabelen om een gedeelde variabele aan te maken, over de objecten van die klasse heen.
Bij methoden om zogenaamde methoden-bibliotheken of hulpmethoden aan te maken.
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.
Gegeven volgende klasse:
Als we dit doen:
Dan zien we volgende uitvoer:
Ieder object houdt de stand van z'n eigen variabelen bij. Ze kunne mekaars interne (zowel publieke als private) staat niet veranderen.
We maken de variabele private int leeftijd
static als volgt: private static int leeftijd=1;
.
We krijgen dan:
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:
Dan wordt de uitvoer:
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.
Heb je er al bij stil gestaan waarom je dit kan doen:
Zonder dat we objecten moeten aanmaken in de trend van:
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.
Stel dat we enkele veelgebruikte methoden willen groeperen en deze gebruiken zonder telkens een object te moeten aanmaken dan doen we dit als volgt:
We kunnen deze methoden nu als volgt aanroepen:
Mooi toch.
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:
Merk op dat we de methoden VerminderFiets
enkel via de klasse kunnen aanroepen:
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!
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:
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.
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:
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.
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.
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";
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.
Uiteraard kan je dit dus gebruiken om op eender welk char
te splitsen en dus nit enkel een ','
(komma).
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:
Voorgaande methoden zijn static
en moet je dus via de klasse String
doen en niet via de objecten zelf.
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
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
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:
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.
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:
Volgende code zal dit bestand uitlezen en de individuele data op het scherm tonen:
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:
Volgende code toont hoe we een bestand van een specifieke locatie kunnen downloaden:
Dit bestand is 1 platte tekst. Willen we deze vervolgens verwerken dan moeten we deze splitsen in lijnen:
We hebben nu een for
nodig die lijn per lijn zal splitsen:
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.
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:
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:
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.
Wanneer een C# applicatie wordt uitgevoerd krijgt het twee soorten geheugen toegewezen dat het 'naar hartelust' kan gebruiken:
Het kleine, maar snelle stack geheugen
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:
Value types
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 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:
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:
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:
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:
Wat gebeurt er hier?
new Student()
: new
roept de constructor van Student
aan. Deze zal een constructor in de heap aanmaken en vervolgens de geheugenlocatie teruggeven.
Een variabele stud
wordt in de stack aangemaakt.
De geheugenlocatie uit de eerste stap wordt vervolgens in stud
opgeslagen in de stack.
Bij arrays
Maar ook bij arrays:
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:
We zullen dus als output krijgen:
Hetzelfde gedrag zien we bij objecten:
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:
We krijgen als uitvoer:
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!
De output bewijst dit:
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:
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:
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 info, lees zeker volgende artikels:
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()
.
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")
.
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!
De default constructor is een constructor die geen extra parameters aanvaardt. Een constructor bestaat ALTIJD uit volgende vorm:
Dit semester is iedere constructor altijd public
(meer info)
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.
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:
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.
Net zoals bij overloading van methoden kunnen we ook constructors overloaden. De code is verrassen gelijkaardig als bij method overloading:
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:
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:
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:
Je hoeft het ref keyword niet mee te geven, dit gebeurt impliciet reeds
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.
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!
En zal volgende error genereren:
Volgend voorbeeld toont een methode die alle getallen van de array op het scherm zal tonen:
Stel dat je elders volgende array hebt int[] leeftijden = {2, 5, 1, 6};
. De ToonArray
methode aanroepen kan dan als volgt:
En de output zal dan zijn:
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):
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:
gebruik van expliciete constructoren
Zorg ervoor dat elke Pokémon bij constructie zijn eigenschappen krijgt.
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.
(Hier is geen verschil met hoe dit er in hoofdstuk 9 uitzag.)
gebruik van expliciete constructoren
herbruik van bestaande constructor
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.
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.
verschil tussen klasse en object
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.
Bij een aanval van een Pokémon met een bepaald type, gaat er een teller omhoog. Er is één teller per PokéType
.
(Merk op: er zijn twee Bulbasaurs in het spel, maar alle aanvallen van graspokémon zijn samen geteld.)
goed gebruik van Random
static
constructoren
We schrijven een digitale tombola. Iedere keer een lotje wordt aangemaakt, wordt er een willekeurig getal aan toegekend.
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.
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:
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:
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:
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.
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 .
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:
Schrijf een methode VermenigvuldigMatrix die 2 matrices als invoer verwacht en als resultaat een nieuwe matrix teruggeeft die het product van beide matrices bevat.
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 )
Volgende opgave komt uit Exercism.io
" 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()
).
Maak een spel, vergelijkbaar met galgje, waarin de speler een woord moet raden. Zie Wiki voor de spelregels indien je deze niet kent.
Voorbeeld output:
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)
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
.
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.
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:
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):
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:
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:
Je kan dus ook methoden schrijven die meegegeven objecten aanpassen daar we deze by reference doorsturen. Een voorbeeld:
Als we deze methode als volgt aanroepen:
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:
Stel dat we volgende methode hebben
Je mag deze methode dus ook oproepen als volgt (we gebruiken de Meting
objecten m1
en m2
uit vorige paragraaf):
Het type van de property Temperatuur
is int
en mag je dus als parameter aan deze methoden meegeven.
Weer hetzelfde verhaal: ook klassen mogen het resultaat van een methoden zijn.
Deze methode kan je dan als volgt gebruiken:
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:
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.
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.
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.
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.
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.
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.
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
.
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.
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:
foreach
combinatie controlestructuren
Pas je oefening met CRUD-operaties op Student
aan zodat we ook het gemiddelde per vak kunnen opvragen.
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.
Nadat er al drie studenten zijn aangemaakt, met 12, 17 en 19 op communicatie:
List
Gebruik je eerdere code voor PlayingCard
om een spelletje "hoger", "lager" toe te voegen.
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()
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.
Een array van objecten gebeurt als volgt:
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:
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.
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
):
Let op de kommapunt helemaal achteraan. Die wordt als eens vergeten.
Van zodra een object in de array staat kan je deze vanuit de array aanspreken d.m.v. de index :
Ook arrays mag je als parameters en returntype gebruiken in methoden. De werking hiervan is identiek aan die van value-types.
In C# kunnen we geen objecten aanmaken voor we een klasse hebben gedefinieerd dat de algemene eigenschappen (properties) en werking (methoden) beschrijft.
Een klasse heeft de volgende vorm:
Volgende code beschrijft de klasse auto in C#
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.
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!
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:
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.
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)
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:
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 van bestaande klassen laten zien
toegang tot properties en methodes demonstreren
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.
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
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.
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
werken met methodes
methodes overschrijfbaar maken
code specifieker maken met behulp van subklassen
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.
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
Zie je geen €-symbool in je output? In het eerste semester hebben we gezien hoe je het gebruik van Unicode activeert.
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.
herbruik code ouderklasse
virtual, override, base
Onze rapporten van studenten missen belangrijke informatie. We willen te zien krijgen wie het statuut van werkstudent heeft.
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:
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.
foreach
(h11-prijzen)foreach
combinatie controlestructuren
We willen enkele gegevens (prijzen) inlezen van de gebruiker en slechts sommige van deze prijzen tonen.
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.
foreach
genest
List
We willen een kaartenspel programmeren. Om dat te doen, moeten we zeker een lijst kaarten kunnen aanmaken en ook kaarten te kunnen trekken.
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.
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).
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:
gegevens van de studenten tonen
een nieuwe student toevoegen
gegevens van een bepaalde student aanpassen
een student uit het systeem verwijderen
stoppen
Je mag voorlopig veronderstellen dat de gebruiker geldige indexposities en gegevens invoert.
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:
Deze keuze voegt een nieuwe student toe.
Voorbeeldinteractie
Bij nul studenten:
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:
Voorbeeldinteractie
Nadat er al twee studenten zijn aangemaakt:
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.
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
Opgelet: wanneer we "x heeft een y" zeggen gaat het niet over overerving, maar over compositie.
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:
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.
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
.
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:
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'
.
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.
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).
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.
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:
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.
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:
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:
GetType wordt aangeroepen en die output krijg je terug.
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)
.
Ook deze methode kan je dus overriden om twee objecten met elkaar te testen. Op het einde van deze cursus zal dieper in Equals
ingaan worden om objecten te vergelijken, maar we tonen hier reeds een voorbeeld:
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.
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
.
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.
Bekijk volgende StackOverflow post 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 einde van deze cursus gaan we dieper in bovenstaande materie in om een volledige Equals
methode op te bouwen en we bij iedere stap uitgebreide uitleg geven.
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:
Volgende lijn zal een error geven: Dier hetDier = new Dier();
We mogen echter wel klassen overerven van deze klasse en instanties van aanmaken:
En dan zal dit wel werken: Wolf wolfje= new Wolf();
En als we polymorfisme gebruiken (zie verder) dan mag dit ook: Dier paardje= new Paard();
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
:
Merk op dat er geen accolades na de signature komen.
Child-klassen zijn verplicht deze abstracte methoden te overriden.
De Paard-klasse wordt dan:
(en idem voor de wolf-klasse uiteraard)
Van zodra een klasse een abstracte methode of property heeft dan ben je, logischerwijs, verplicht om de klasse ook abstract te maken.
Properties kunnen virtual gemaakt, en dus ook abstract
. Volgende voorbeeld toont hoe dit werkt:
Uitgewerkt voorbeeld Abstract en System.Object mbv Zoo-dieren (compilatie uit hoorcollege 18-19)
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:
Ook properties kan je virtual instellen en override'n.
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 hier). Je schrijft dus best manueel de override van properties
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.