All pages
Powered by GitBook
1 of 8

H8: Klassen en objecten

OOP Intro

OOP

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

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

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

Om het concept van objecten te illustreren wordt meestal een voorwerp uit het dagelijks leven als voorbeeld gebruikt. Neem bijvoorbeeld een auto. De auto is het object en dit object heeft bepaalde properties of eigenschappen. Een eigenschap van de auto kan een band, een deur, een spiegel, de uitlaat of eender welk ander onderdeel van de auto zijn. Maar een auto heeft ook methoden. Een methode kan starten of remmen zijn.

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. Bron van deze introductie.

Black-box principe

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

Binnen OOP wordt dit blackbox-concept encapsulatie genoemd. Het doel van OOP is andere programmeurs (en jezelf) zoveel mogelijk af te schermen van de interne werking van je code. Vergelijk het met de methoden uit vorig semester: "if it works, it works" en dan hoef je niet in de code van de methode te gaan zien wat er juist gebeurt.

Klassen en objecten

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

Wanneer we meerdere objecten gebruiken van dezelfde soort dan kunnen we zeggen dat deze objecten allemaal deel uitmaken van een zelfde klasse.

Zo hebben we bijvoorbeeld de klasse van de auto's. De verschillende auto's die je op de straat ziet rijden zijn allemaal objecten van die klasse. De klasse zelf is een soort algemene beschrijving waaraan alle objecten van dat type moeten voldoen (bv: alle auto's hebben 4 banden, 1 motor en kunnen sneller en trager rijden).

Definitie klasse en object

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

  • Een individueel object is een instantie van een klasse

Je zou dit kunnen vergelijken met het grondplan voor een huis dat tien keer in een straat zal gebouwd worden. Het plan met alle soortgelijke eigenschappen van ieder huis is de klasse. De effectieve huizen die we, gebaseerd op dat grondplan, bouwen zijn de instanties of objecten van deze klasse.

Nog een andere invalshoek:

Een andere invalshoek is de zogenaamde "dungeons" in veel online games. De makers van het spel hebben iedere dungeon in een klasse beschreven. Wanneer een groep avonturiers nu in zo'n grot gaat dan wordt voor die groep een aparte instantie (instance) van die grot gemaakt, gebasseerd op de klasse. Ze doen dit zodat iedere groep spelers mekaar niet voor de voeten loopt in 1 grot.

Objecten in de woorden van Steve Jobs

Steve Jobs, de oprichter van Apple, was een fervent fan van OOP. In een interview, way back, gaf hij volgende uitstekende uitlegbron:

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:

Bron

Klassen en objecten in C#

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

Klasse maken

Een klasse heeft de volgende vorm:

[optionele access modifier] class className
{

}

Volgende code beschrijft de klasse auto in C#

class Auto
{

}

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

De optionele access modifier komen we later op terug.

Klassen in Visual Studio

Je kan "eender waar" een klasse aanmaken, maar het is een goede gewoonte om per klasse een apart bestand te gebruiken:

  • In de solution explorer, rechterklik op je project

  • Kies Add

  • Kies Class..

  • Geef een goede naam voor je klasse

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

Klasse toevoegen in VS

Objecten aanmaken

Je kan nu objecten aanmaken van de klasse die je hebt gedefinieerd. Je doet dit door eerst een variabele te definiëren en vervolgens een object te instantiëren met behulp van het new keyword:

Auto mijnEerste = new Auto();
Auto mijnAndereAuto = new Auto();

We hebben nu twee objecten aangemaakt van het type Auto.

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

  • Klassen maak je aan als aparte files in je project

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

basics oop same in vv

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.

Methoden en access modifiers

Een eenvoudige klasse

We zullen nu enkele basisconcepten van klassen en objecten toelichten aan de hand van praktische voorbeelden.

Object methoden

Stel dat we een klasse willen maken die ons toelaat om objecten te maken die verschillende mensen voorstellen. We willen aan iedere mens kunnen vragen waar deze woont en die zal dat dan op het scherm tonen.

We maken een nieuwe klasse Mens en maken een methode Praat die iets op het scherm zet:

class Mens
{
    public void Praat()
    {
        Console.WriteLine("Ik ben een mens!");
    }
}

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:

Mens joske = new Mens();
Mens alfons = new Mens();

joske.Praat();
alfons.Praat();

Er zal 2x Ik ben een mens! op het scherm verschijnen. Waarbij telkens ieder object (joske en alfons) zelf verantwoordelijk ervoor waren dat dit gebeurde.

Public en private access modifiers

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

Test in de voorgaande klasse eens wat gebeurt wanneer je public verwijderd voor de methode. Inderdaad, je zal de methode Praat niet meer op de objecten kunnen aanroepen.

De reden: wanneer je voor een methode (of klasse) niet expliciet public zet, dan kan deze methode niet van uit alle andere klassen worden aangeroepen.

Test volgende klasse eens, kan je de methode VertelGeheim vanuit de Main op joske aanroepen?

class Mens
{
    public void Praat()
    {
        Console.WriteLine("Ik ben een mens!");
    }

    void VertelGeheim()
    {
        Console.WriteLine("Mijn geheim is dat ik leef!");
    }
}

Als je duidelijk wil maken dat bepaalde code niet van buitenaf aangeroepen kan worden, schrijf dan private in plaats van public. Als je geen van beide schrijft, zit je code ergens tussenin public en private (zie later). Als beginnende programmeur maak je er best een gewoonte van duidelijk te kiezen voor public of private.

Reden van private

Waarom zou je bepaalde zaken private maken?

Stel dat we in de klasse extra (hulp)methoden willen gebruiken die enkel intern nodig zijn, dan doen we dit. Volgende voorbeeld toont hoe we in de methode Praat de private methode VertelGeheim gebruiken:

class Mens
{
    public void Praat()
    {
        Console.WriteLine("Ik ben een mens!");
        VertelGeheim();
    }

    void VertelGeheim()
    {
        Console.WriteLine("Mijn geheim is dat ik leef!");
    }
}

Als we nu elders een object laten praten als volgt:

Mens rachid = new Mens();
rachid.Praat();

Dan zal de uitvoer worden:

Ik ben een mens!
Mijn geheim is dat ik leef!

Instantievariabelen

We maken onze klasse wat groter, we willen dat een object een leeftijd heeft die we kunnen verhogen via een methode VerjaardagVieren (we hebben de methode VertelGeheim even weggelaten):

class Mens
{
    private int leeftijd = 1;

    public void VerjaardagVieren()
    {
        Console.WriteLine("Hiphip hoera voor mezelf!");
        leeftijd++;
        Praat();
    }

    public void Praat()
    {
        Console.WriteLine("Ik ben een mens! ");
        Console.WriteLine($"Ik ben {leeftijd} jaar oud");
    }

}

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:

Mens Elvis = new Mens();
Mens Bono = new Mens();

Elvis.VerjaardagVieren();
Elvis.VerjaardagVieren();
Evlis.VerjaardagVieren();
Bono.VerjaardagVieren();

Als je deze code zou uitvoeren zal je zien dat de leeftijd van Elvis verhoogt en niet die van Bono wanneer we VerjaardagVieren aanroepen. Zoals het hoort!

Initiële waarde van een instantievariabele

Bekijk opnieuw het voorbeeld:

class Mens
{
    private int leeftijd = 1;

    public void VerjaardagVieren()
    {
        Console.WriteLine("Hiphip hoera voor mezelf!");
        leeftijd++;
        Praat();
    }

    public void Praat()
    {
        Console.WriteLine("Ik ben een mens! ");
        Console.WriteLine($"Ik ben {leeftijd} jaar oud");
    }

}

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.

Properties

Properties

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

  1. Full properties

  2. Auto properties

In een wereld zonder properties

Properties (eigenschappen) zijn de C# manier om objecten hun interne staat in en uit te lezen. Ze zorgen voor een gecontroleerde toegang tot de interne structuur van je objecten.

Stel dat we volgende klasse hebben:

public class SithLord
{
    private int energy;
    private string sithName;
}

Een SithLord heeft steeds een verborgen Sith Name en ook een hoeveelheid energie die hij nodig heeft om te strijden. Het is uit den boze dat we eenvoudige data fields (energy en name) public maken. Zouden we dat wel doen dan kunnen externe objecten deze geheime informatie uitlezen!

SithLord palpatine = new SithLord();
Console.WriteLine(palpatine.sithName); //DIT ZAL DUS NIET WERKEN, daar sithName private is.

We willen echter wel van buiten uit het energy-level van een SithLord kunnen instellen. Maar ook hier hetzelfde probleem: wat als we de energy-level op -1000 instellen? Terwijl energy nooit onder 0 mag gaan.

Properties lossen dit probleem op

Oldschool oplossing

Vroeger loste men voorgaande probleem op door Get-methoden te schrijven:

Je zal deze manier nog in veel andere talen tegenkomen. Wij prefereren properties zoals we nu zullen uitleggen.

public class SithLord
{
    private int energy;
    private string sithName;

    public void SetSithName(string newname)
    {
        sithName = newname;
    }

    public string GetSithName()
    {
        return "YOU WISH!";
    }

    public void SetEnergy(int value)
    {
        if(value > 0 && value < 9999)
            energy = value;
    }

    public int GetEnergy()
    {
        return energy;
    }
}

Je zou dan kunnen doen:

SithLord vader = new SithLord();
vader.SetEnergy(20); 
Console.WriteLine($"Vaders energy is {vader.GetEnergy()}"); //get

Full properties

Een full property ziet er als volgt uit:

class SithLord
{
    private int energy;
    private string sithName;

    public int Energy
    {
        get
        {
            return energy;
        }
        set
        {
            energy = value;
        }
    }
}

Dankzij deze code kunnen we nu elders dit doen:

SithLord vader = new SithLord();
vader.Energy = 20; //set
Console.WriteLine($"Vaders energy is {Vader.Energy}"); //get

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

We zullen de property nu stuk per stuk analyseren:

  • public int Energy: een property is normaal public. Vervolgens zeggen we wat voor type de property moet zijn en geven we het een naam. Indien je de property gaat gebruiken om een intern dataveld naar buiten beschikbaar te stellen, dan is het een goede gewoonte om dezelfde naam als dat veld te nemen maar nu met een hoofdletter. (dus Energy i.p.v. energy).

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

  • get {}: indien je wenst dat de property data naar buiten moet sturen, dan schrijven we de get-code. Binnen de accolades van de get schrijven we wat er naar buiten moet gestuurd worden. In dit geval return energy maar dit mag even goed bijvoorbeeld return 4 of een hele reeks berekeningen zijn. Het element dat je returnt in de get code moet uiteraard van hetzelfde type zijn als waarmee je de property hebt gedefinieerd (int in dit geval).

    • We kunnen nu van buitenaf toch de waarde van energy uitlezen via de property en het get-gedeelte: Console.WriteLine(palpatine.Energy);

  • set{}: in het set-gedeelte schrijven we de code die we moeten hanteren indien men van buitenuit een waarde aan de property wenst te geven om zo een interne variabele aan te passen. De waarde die we van buitenuit krijgen (als een parameter zeg maar) zal altijd in een lokale variabele value worden bewaard. Deze zal van het type van de property zijn. Vervolgens kunnen we value toewijzen aan de interne variabele indien gewenst: energy=value

    • We kunnen vanaf nu van buitenaf waarden toewijzen aan de property en zo energyvtoch bereiken: palpatine.Energy=50.

Snel property schrijven

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

Full property met toegangscontrole

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

Properties hebben echter de mogelijkheid om op te treden als wachters van en naar de interne staat van objecten.

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

   public int Energy
    {
        get
        {
            return energy;
        }
        set
        {
            if(value >= 0)
                energy = value;
        }
    }

Enkel indien de toegewezen waarde groter of gelijk is aan 0 zal deze ook effectief aan energy toegewezen worden. Volgende lijn zal dus geen effect hebben: Palpatine.Energy=-1;

We kunnen de code binnen set (en get) zo complex als we willen maken.

Property variaties

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

Write-only property

   public int Energy
    {
        set
        {
            if(value >= 0)
                energy = value;
        }
    }

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

Read-only property

   public int Energy
    {
        get
        {
            return energy;
        }
    }

We kunnen dus enkel energy van buitenuit uitlezen, maar niet aanpassen.

Opgelet: het readonly keyword heeft andere doelen en wordt NIET gebruikt in C# om een readonly property te maken

Read-only property met private set

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

   public int Energy
    {
        get
        {
            return energy;
        }
        private set
        {
            if(value >= 0)
                energy = value;
        }
    }

Van buitenuit zal enkel code werken die deget-van deze property aanroept: Console.WriteLine(palpatine.Energy);. Code die de set van buitenuit nodig heeft zal een fout geven zoals: palpatine.Energy=65; ongeacht of deze geldig is of niet.

Nu goed opletten: indien we in het object de property willen gebruiken dan moeten we deze dus ook effectief aanroepen, anders omzeilen we hem als we rechtstreeks energy instellen.

Kijk zelf naar volgende slechte code:

class SithLord
{
    private int energy;
    private string sithName;

    public void ResetLord()
    {
        energy = -1;
    }

    public int Energy
    {
        get
        {
            return energy;
        }
        private set
        {
            if(value >= 0)
                energy = value;
        }
    }
}

De nieuw toegevoegde methode ResetLord willen we gebruiken om de lord z'n energy terug te verlagen. Door echter energy=-1 te schrijven geven we dus -1 rechtstreeks aan energy. Nochtans is dit een illegale waarde. We moeten dus in de methode ook expliciet via de property gaan en dus schrijven:

public void ResetLord()
{
    Energy = -1; // Energy i.p.v. energy
}

Het is een goede gewoonte om zo vaak mogelijk via de properties je interne variabele aan te passen en niet rechtstreeks het dataveld zelf.

Read-only Get-omvormers

Je bent uiteraard niet verplicht om voor iedere interne variabele een bijhorende property te schrijven. Omgekeerd ook: mogelijk wil je extra properties hebben voor data die je 'on-the-fly' kan genereren.

Stel dat we volgende klasse hebben

public class Person
{
    private string firstName;
    private string lastName;
}

We willen echter ook soms de volledige naam op het scherm tonen ("Voornaam + Achternaam"). Via een read-only property kan dit supereenvoudig:

public class Person
{
    private string firstName;
    private string lastName;
    public string FullName
    {
        get{ return $"{firstName} {lastName}";}
    }
    public string Email
    {
        get
        {
            return $"{firstName}.{lastName}@ap.be";
        }
    }
}

Of nog eentje:

public class Earth
{
    public double GravityConstant
    {
        get
        {
            return 9.81;
        }
    }
}

Nog een voorbeeldje:

public class Person
{
    private int age;

    public bool IsProbablyAlive
    {
        get
        {
            if(age > 120) return false;
            return true;
        }
    }
}

Vaak gebruiken we dit soort read-only properties om data te transformeren. Stel bijvoorbeeld dat we volgende klasse hebben:

public class Person
{
    private int age; //in jaren

    public int AgeInMonths
    {
        get
        {
            return age * 12;
        }
    }
}

Auto properties

Automatische eigenschappen (autoproperties) in C# staan toe om eigenschappen (properties) die enkel een waarde uit een private variabele lezen en schrijven verkort voor te stellen.

Zo kan je eenvoudige de klasse Person herschrijven met behulp van autoproperties. De originele klasse:

public class Person
    {

        private string firstName;
        private string lastName;
        private int age;

        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
            }
        }

        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
            }
        }

        public int Age
        {
            get
            {
                return age;
            }
            set
            {
                age = value;
            }
        }
    }

De herschreven klasse met autoproperties (autoprops):

public class Person
    {

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }

    }

Beide klassen hebben exact dezelfde functionaliteit, echter de klasse aan de rechterzijde is aanzienlijk eenvoudig om te lezen en te typen.

Beginwaarde van autoprops

Je mag autoproperties beginwaarden geven door de waarde achter de property te geven, als volgt:

public int Age {get;set;} = 45;

Altijd auto-properties?

Merk op dat je dit enkel kan doen indien er geen extra logica in de property aanwezig is. Stel dat je bij de setter van age wil controleren op een negatieve waarde, dan zal je dit zoals voorheen moeten schrijven en kan dit niet met een automatic property:

set
{
    if( value > 0)
        age = value;
}

Voorgaande property kan dus NIET herschreven worden met een automatic property.

Alleen-lezen eigenschap

Je kan automatic properties ook gebruiken om bijvoorbeeld een read-only property te definiëren . Als volgt:

Originele klasse:

public string FirstName
{

    get
    {
        return firstName;
    }
}

Met autoprops:

public string FirstName { get; private set;}

En andere manier die ook kan is als volgt:

public string FirstName { get; }

De enige manier om FirstName een waarde te geven is via de constructor van de klasse. Alle andere manieren zal een error genereren.Meer info.

Snel autoproperties typen in Visual Studio:

Als je in Visual Studio in je code prop typt en vervolgens twee keer de tabtoets indrukt dan verschijnt al de nodige code voor een automatic property. Je hoeft dan enkel nog volgende zaken in orde te brengen:

  • Het type van de property

  • De naam van de property (identifier)

  • De toegankelijkheid van get/set (public, private, protected)

Via propg krijg je aan autoproperty met private setter.

Methode of property

Een veel gestelde vraag bij beginnende OO-ontwikkelaars is: "Moet dit in een property of in een methode gestoken worden?"

De regel is eenvoudig:

  • Betreft het een actie, iets dat het object moet doen (tekst tonen, iets berekenen, etc) dan plaats je het in een methode

  • Betreft het een eigenschap die een bepaalde waarde heeft, dan gebruik je een property

Hier een iets meer uitgebreid PRO antwoord.

Kennisclip

  • Properties

  • Full properties

  • Auto properties

DateTime: leren werken met objecten

DateTime

De .NET klasse DateTime is de ideale manier om te leren werken met objecten. Het is een nuttige en toegankelijk klasse.

Grote delen van deze tekst komen van zetoode.com.

DateTime objecten aanmaken

Er zijn 2 manieren om DateTime objecten aan te maken:

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

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

DateTime.Now

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

        DateTime currentTime = DateTime.Now;
        Console.WriteLine(currentTime);

Met constructor

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:

DateTime birthday = new DateTime(1982, 3, 18); //year, month, day

DateTime someMomentInTime = new DateTime(2017, 1, 18, 10, 16,34 ); //year, month, day, hour, min, sec

DateTime methoden

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

Add Methods

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

  • AddDays

  • AddHours

  • AddMilliseconds

  • AddMinutes

  • AddMonths

  • AddSeconds

  • AddTicks

  • AddYears

Een voorbeeld:

DateTime timeNow= DateTime.Now;

DateTime nextWeek= timeNow.AddDays(7);

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

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

DateTime someTime= new DateTime(2019, 4, 1);

//much later...
someTime = someTime.AddYears(10);
Console.WriteLine(someTime);

DateTime properties

Properties zijn een zeer uniek aspect van C# zoals we in vorig hoofdstuk zagen. We zullen deze nog tot in den treuren leren maken. Via properties kan je de interne staat van objecten uitlezen én aanpassen, dit op een gecontroleerde manier.

Het fijne aan properties is dat :

  • je gebruikt ze alsof het gewone variabelen zijn

  • maar ze werken als methoden

Meer hierover later.

Enkele nuttige properties van DateTime zijn:

  • Date

  • Day

  • DayOfWeek

  • DayOfYear

  • Hour

  • Millisecond

  • Minute

  • Month

  • Second

  • Ticks

  • TimeOfDay

  • Today

  • UtcNow

  • Year

Properties gebruiken

Alle properties van DateTime zijn read-only.

Een voorbeeld:

DateTime moment = new DateTime(1999, 1, 13, 3, 57, 32, 11);

// Year gets 1999.
int year = moment.Year;

// Month gets 1 (January).
int month = moment.Month;

// Day gets 13.
int day = moment.Day;

// Hour gets 3.
int hour = moment.Hour;

// Minute gets 57.
int minute = moment.Minute;

// Second gets 32.
int second = moment.Second;

// Millisecond gets 11.
int millisecond = moment.Millisecond;

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

DateTime now = DateTime.Now;

Console.WriteLine($"The current day is {now.DayOfWeek}");

Datum en tijd formateren

Je hebt een invloed op hoe DateTime objecten naar string worden opgezet. Je kan dit door door extra formatter syntax mee te geven.

Dit zie je in volgende voorbeeld:

DateTime now = DateTime.Now;

WriteLine(now.ToString("d")); // short date 
WriteLine(now.ToString("D")); // long date
WriteLine(now.ToString("F")); // full date and time
WriteLine(now.ToString("M")); // month and day
WriteLine(now.ToString("o")); // date en time separated by T and time zone at the end
WriteLine(now.ToString("R")); // RFC1123 date and time
WriteLine(now.ToString("t")); // short time
WriteLine(now.ToString("T")); // long time
WriteLine(now.ToString("Y")); // year and month

Custom format

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

Dit wordt hier volledig uit de doeken gedaan.

Localized time

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

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

DateTime now = DateTime.Now;
CultureInfo russianCI = new CultureInfo("ru-RU");

Console.WriteLine($"Current time in Russian style is: {now.ToString("F", russianCI)}");

Culture names

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.

Static method

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

Parsing time

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

string date_string = "8/11/2016"; //dit zou dus ook door gebruiker kunnen ingetypt zijn
DateTime dt = DateTime.Parse(date_string);
Console.WriteLine(dt);

Zoals je ziet roepen we Parse aan op DateTime en dus niet op een specifiek object.

IsLeapYear

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

DateTime today= DateTime.Now;
bool isLeap= DateTime.IsLeapYear(today.Year);
if(isLeap==true)
    Console.WriteLine("This year is a leap year");

TimeSpan (PRO)

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

DateTime today = DateTime.Today;
DateTime borodino_battle = new DateTime(1812, 9, 7);

TimeSpan diff = today - borodino_battle;

WriteLine("{0} days have passed since the Battle of Borodino.", diff.TotalDays);

Oefening

Klokje

Maak een applicatie die bestaat uit een oneindige loop. De loop zal iedere seconde pauzeren: System.Threading.Thread.Sleep(1000);. Vervolgens wordt het scherm leeg gemaakt en wordt de huidige tijd getoond. Merk op dat ENKEL de tijd wordt getoond, niet de datum.

Verjaardag

Maak een applicatie die aan de gebruiker vraagt op welke dag hij jarig is. Toon vervolgens over hoeveel dagen z'n verjaardag dan zal zijn.

(Klassikale!) smaakmaker OOP

1. Keuzemenuutje voor latere programma's

Leerdoelen

  • Code makkelijk toegankelijk maken

  • Vermijden heleboel piepkleine projectjes aan te maken

Functionele analyse

We gaan leren werken met grotere hoeveelheden code. We zullen voortaan niet meer al onze programma's in een apart project plaatsen, maar een keuzemenu gebruiken om daaruit alle andere programma's op te starten.

Technische analyse

Wanneer de gebruiker de Main van de klasse Program opstart, krijg je een keuzemenuutje. Hierop worden omschrijvingen getoond voor alle oefeningen die je al hebt, gevolgd door de unieke titel van de oefening in kwestie. Wanneer de gebruiker het getal intypt dat overeenstemt met een oefening, wordt de oefening opgestart.

Voorbeeldoutput:

Wat wil je doen? 1. Vormen tekenen (h8-vormen) 2. Auto's laten rijden (h8-autos) 3. Patiënten tonen (h8-patienten) 4. Honden laten blaffen (h8-blaffende-honden)

Wanneer de gebruiker een van deze opties kiest door het juiste getal in te typen, wordt dat programma opgestart. Als hij iets intypt dat niet kan worden uitgevoerd, komt er een boodschap ongeldig getal!. Wanneer je dit programma voor het eerst schrijft, kan je alleen nog maar ongeldige keuzes maken.

2. Kennismaking OOP (h8-vormen)

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.

3. Voorbeeld met auto's (h8-autos)

Doelstelling

  • Kennismaking met OOP

Functionele analyse

We bouwen een simulatie van rijdende auto's. We voorzien deze van een kilometerteller, een huidige snelheid, een methode om de gaspedaal te simuleren en om de rem te simuleren.

Technische analyse

  • We starten vanaf de klasse Auto uit de cursus (maar noemen deze Car).

  • We voegen eigenschappen Odometer en Speed toe, beide doubles.

  • We zorgen dat de snelheid nooit onder de 0km/u kan gaan en nooit boven de 120km/u door de eigenschap aan te passen.

  • We schrijven klassikaal een methode Gas() die de snelheid met 10km/u verhoogt en de auto verplaatst met zijn gemiddelde snelheid / 60 (om één minuut rijden te simuleren). De gemiddelde snelheid is het gemiddelde van de snelheid voor en na het verhogen.

  • We schrijven individueel een methode Brake die gelijkaardig werkt, maar die de snelheid met 10 km/u verlaagt.

  • We schrijven een methode Main

    • die twee auto's aanmaakt

    • de eerste vijf keer laat versnellen, drie keer laat remmen

    • de tweede tien keer laat versnellen en een keer laat remmen

    • in onderstaand formaat de snelheid en afgelegde weg afprint: auto 1: km/u, afgelegde weg km auto 2: km/u, afgelegde weg km

4. Refactoren van deze code (met allerlei arrays voor eigenschappen) die een verslag produceert met info (h8patienten)

Doelstelling

  • Kennismaking met OOP

  • Kennismaking met refactoring

  • Toepassing van encapsulatie

Functionele analyse

We hebben al wat code, maar we vinden deze moeilijk te onderhouden omdat we allerlei variabelen moeten onderhouden. We refactoren deze om zo een meer objectgericht en beter onderhoudbaar programma te bekomen.

Technische analyse

  • Introduceer een klasse Patient

  • Vervang alle arrays door één array van objecten van de klasse Patient

  • Zorg dat het afgeprinte verslag er identiek uitziet aan het oorspronkelijke verslag

Code om van te starten:

using System; 

namespace OOP {     
   class PatientProgram {         
      public static void Main() {             
         Console.WriteLine("Hoe veel patiënten zijn er?"); // aanmaken van de nodige variabelen
         int numberOfPatients = int.Parse(Console.ReadLine());
         string[] patientNames = new string[numberOfPatients];
         string[] patientGenders = new string[numberOfPatients];
         string[] patientLifestyles = new string[numberOfPatients];
         int[] patientDays = new int[numberOfPatients];
         int[] patientMonths = new int[numberOfPatients];
         int[] patientYears = new int[numberOfPatients];

         // invullen van de rijen
         for(int i = 0; i < numberOfPatients; i++) {
            patientNames[i] = Console.ReadLine();
            patientGenders[i] = Console.ReadLine();
            patientLifestyles[i] = Console.ReadLine();
            patientDays[i] = int.Parse(Console.ReadLine());
            patientMonths[i] = int.Parse(Console.ReadLine());
            patientYears[i] = int.Parse(Console.ReadLine());
         }

         // afprinten van het verslag
         // variabele in twee keer toegekend om code niet te breed te maken
         for(int i = 0; i < numberOfPatients; i++) {
            string info = $"{patientNames[i]} ({patientGenders[i]}, {patientLifestyles[i]})";
            info += $", geboren {patientDays[i]}-{patientMonths[i]}{patientYears[i]}";
            Console.WriteLine(info);
         }
      }
   }
}

5. Voorbeeld abstractie (h8-honden)

Doelstelling

  • Kennismaking met OOP Kennismaking met refactoring

  • Toepassing van abstractie

Functionele analyse

We krijgen een programma dat al objecten bevat, maar dit programma moet zelf nog veel rekening houden met hoe deze objecten in elkaar zitten. We refactoren het om zo een meer objectgericht en beter onderhoudbaar programma te bekomen.

Technische analyse

Je krijgt volgende twee files. De bestandsnamen volgen de afspraak:

using System; 

namespace OOP {
   class BarkingProgram {
      // nu maken we onze randomgenerator *buiten* Main
      public static Random rng = new Random();
      public static void Main() {
         BarkingDog dog1 = new BarkingDog();
         BarkingDog dog2 = new BarkingDog();
         dog1.Name = "Swieber";
         dog2.Name = "Misty";
         dog1BreedNumber = rng.Next(0,3);
         dog2BreedNumber = rng.Next(0,3);
         if(dog1BreedNumber == 0) {
            dog1.Breed = "German Shepherd";
         }             
         else if(dog1BreedNumber == 1) {
            dog1.Breed = "Wolfspitz";
         }
         else {
            dog1.Breed = "Chihuahua";
         }
         if(dog2BreedNumber == 0) {
            dog2.Breed = "German Shepherd";
         }
         else if(dog2BreedNumber == 1) {
            dog2.Breed = "Wolfspitz";
         }
         else {
            dog2.Breed = "Chihuahua";
         }
         while(true) {
            Console.WriteLine(dog1.Bark());
            Console.WriteLine(dog2.Bark());
         }
      }
   }
}

en

namespace OOP {
    class BarkingDog {
        public string Name;
        public string Breed;

        public string Bark() {
            if(Breed == "German Shepherd") {
                return "RUFF!";
            }
            else if(Breed == "Wolfspitz") {
                return "AwawaWAF!";
            }
            else if(Breed == "Chihuahua") {
                return "ARF ARF ARF!";
            }
            // dit zou nooit mogen gebeuren
            // maar als de programmeur van Main iets fout doet, kan het wel
            else {
                return "Euhhh... Miauw?";
            }
        }
    }
}

Volg hierbij volgende stappen:

  • Maak de random generator statisch onderdeel van de klasse BarkingDog.

  • Voeg volgende code toe binnen de klasse BarkingDog:

public BarkingDog() {
    // bepaal hier met de randomgenerator het ras van de hond
    // stel hier `Breed` in
    // maak van `Breed` een eigenschap zoals we dat bij de geometrische vormen hebben gedaan
}

6. Controleren dat alles kan worden opgestart via keuzemenu.

7. Alles in Git plaatsen en delen met de lector. Eerst controleren dat juiste files genegeerd worden met git status -u!

Oefeningen

Richtlijnen

Structuur oefeningen

Vanaf hier veronderstellen we dat je in één groot project werkt dat één klasse Program heeft. De oefeningen worden los van elkaar omschreven, maar je zorgt ervoor dat ze getest kunnen worden via het keuzemenu in je klasse Program.

meerdere Main methodes

Indien je meerdere klassen hebt met een methode Main, moet je aangeven welke normaal gebruikt wordt voor de uitvoering van je programma. Dat doe je door aan je .csproj-bestand het volgende toe te voegen: <StartupObject>Namespace-van-de-opstartklasse.Naam-van-de-opstartklasse</StartupObject>

Dit staat hier niet zomaar! Als je dit niet doet, zullen je oefeningen niet uitgevoerd kunnen worden. Je .csproj file staat in dezelfde map als je .cs-bestanden en indien het niet opent in Visual Studio, kan je het openen in Kladblok.

Bijvoorbeeld:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <StartupObject>OOP.Program</StartupObject>
  </PropertyGroup>
</Project>

(Je file zou er al gelijkaardig moeten uitzien, maar je moet zelf het StartupObject toevoegen.)

Oefening: H8-dag-van-de-week

Leerdoelen

  • aanmaken van DateTime objecten

  • formatteren van DateTime objecten

Functionele analyse

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

Technische analyse

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

  • daarna moet je laten zien over welke dag van de week het gaat

    • gebruik hiervoor formattering van een DateTime

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

    • als je computer niet volledig ingesteld is op Belgisch Nederlands, kan het resultaat er wat anders uitzien.

  • noem de klasse waarin je dit schrijft DayOfWeekProgram

    • schrijf je code in de statische methode Main

    • roep de statische Main van DayOfWeekProgram op in het keuzemenu van Program

Voorbeeldinteractie

Welke dag?
> 14
Welke maand?
> 2
Welk jaar?
> 2020
14 februari 2020 is een vrijdag.

Oefening: H8-ticks-sinds-2000

Leerdoelen

  • aanmaken van DateTime objecten

Functionele analyse

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

Technische analyse

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

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

  • Noem de klasse waarin je dit schrijft Ticks2000Program

Voorbeeldinteractie

Sinds 1 januari 2000 zijn er (hier wordt het aantal getoond) ticks voorbijgegaan.

Oefening: H8-schrikkelteller

Leerdoelen

  • gebruik van een statische property

Functionele analyse

We willen bepalen hoe veel schrikkeljaren er zijn tussen 1800 en 2020.

Technische analyse

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

  • maak gebruik van een statische methode van deze klasse

  • noem je klasse LeapYearProgram en voorzie ze van een Main

Voorbeeldinteractie

Er zijn (hier wordt het aantal getoond) schrikkeljaren tussen 1800 en 2020.

Oefening: H8-simpele-timing

Leerdoelen

  • eenvoudig code leren timen

  • gebruiken van DateTime

  • herhaling arrays

Functionele analyse

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

Technische analyse

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

  • Vul de array in met een for-lus.

  • Noem de klasse waarin je dit schrijft ArrayTimerProgram

Voorbeeldinteractie

Het duurt (hier wordt het aantal getoond) milliseconden om een array van een miljoen elementen aan te maken en op te vullen met opeenvolgende waarden.

Oefening: H8-RapportModule-V1

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van simpele properties en methodes

Functionele analyse

Dit programma geeft op basis van de input van een percentage de graad weer die je met dit gegeven zou behaald hebben.

Technische analyse

Ontwerp een klasse ResultV1 die je zal tonen wat je graad is gegeven een bepaald behaald percentage. Het enige dat je aan een ResultV1-object moet kunnen geven is het behaalde percentage. Enkel het totaal behaalde percentage wordt bijgehouden. Via een methode PrintHonors kan de behaalde graad worden weergegeven. Dit zijn de mogelijkheden:

  • < 50: niet geslaagd;

  • tussen 50 en 68: voldoende;

  • tussen 68 en 75: onderscheiding;

  • tussen 75 en 85: grote onderscheiding;

  • > 85: grootste onderscheiding.

Je hoeft voorlopig geen rekening te houden met ongeldige waarden. Test je klasse door ResultV1 ook van een statische methode Main te voorzien, waarin je enkele objecten van deze klasse aanmaakt en de verschillende properties waarden te geeft en vervolgens PrintHonors oproept.

Oefening: H8-RapportModule-V2

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van simpele properties en methodes

Functionele analyse

Dit programma geeft op basis van de input van een percentage de graad weer die je met dit gegeven zou behaald hebben.

Technische analyse

Ontwerp een klasse ResultV2 die je zal vertellen wat je graad is gegeven een bepaald behaald percentage. Het enige dat je aan een ResultV2-object moet kunnen geven is het behaalde percentage. Enkel het totaal behaalde percentage wordt bijgehouden. Via een methode ComputeHonors kan de behaalde graad worden teruggegeven. Dit werkt op dezelfde manier als in versie 1 van deze oefening, maar de verschillende graden worden voorgesteld met een Enum, Honors. De methode ComputeHonors toont het resultaat niet, maar geeft een waarde van deze Enum terug. Het is aan de Main om deze waarde af te printen, zodat je kan zien of je code werkt.

Test je klasse op dezelfde manier als versie 1. De teruggegeven waarden van Honors mag je in de Main meegeven aan Console.WriteLine.

Oefening: H8-Getallencombinatie

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties

Functionele analyse

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

Technische analyse

Maak een eenvoudige klasse NumberCombination. Deze klasse bevat 2 getallen (type int). Er zijn 4 methoden, die allemaal een double teruggeven:

  • Sum: geeft som van beide getallen weer

  • Difference: geeft verschil van beide getallen weer

  • Product: geeft product van beide getallen weer

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

Gebruik full properties voor Number1 en Number2 en toon in je Main aan dat je code werkt.

Volgende code zou namelijk onderstaande output moeten geven:

NumberCombination pair1 = new NumberCombination();
pair1.Number1 = 12;
pair1.Number2 = 34;
Console.WriteLine("Paar:" + pair1.Number1 + ", " + pair1.Number2);
Console.WriteLine("Sum = " + pair1.Sum());
Console.WriteLine("Verschil = " + pair1.Difference());
Console.WriteLine("Product = " + pair1.Product());
Console.WriteLine("Quotient = " + pair1.Quotient());

Voorbeeldinteractie(s)

Paar: 12, 34
Som = 46
Verschil = -22
Product = 408
Quotient = 0,352941176470588

Oefening: H8-Figuren

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

Functionele analyse

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

Technische analyse

Er is een klasse Rectangle met properties Width en Height en een klasse Triangle met Base en Height. Je programma maakt de figuren die hierboven beschreven worden aan met waarde 1.0 voor elke afmeting en stelt daarna hun afmetingen in via de setters voor deze properties. De oppervlakte wordt bepaald in een read-only property, Surface van het type double.

Indien om het even welk van deze properties wordt ingesteld op 0 (of minder) wordt er een foutboodschap afgeprint en wordt de afmeting niet aangepast.

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

Schrijf de voorbeelden uit in de Main van een extra klasse, FigureProgram.

Voorbeeldinteractie(s)

(Er worden twee rechthoeken en twee driehoeken aangemaakt. De afmetingen van de eerste rechthoek worden eerst op -1 en 0 ingesteld.

Het is verboden een breedte van -1 in te stellen!
Het is verboden een breedte van 0 in te stellen!
Een rechthoek met een breedte van 2,2m en een hoogte van 1,5m heeft een oppervlakte van 3,3m².
Een rechthoek met een breedte van 3m en een hoogte van 1m heeft een oppervlakte van 3m².
Een driehoek met een basis van 3m en een hoogte van 1m heeft een oppervlakte van 1,5m².
Een driehoek met een basis van 2m en een hoogte van 2m heeft een oppervlakte van 2m².

Oefening: H8-Studentklasse

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

Functionele analyse

Dit programma vraagt om de naam en leeftijd van een student. Er moet ook worden meegeven in welke klasgroep de student is ingeschreven. De groepen zijn EA1, EA2 en EB1. Vervolgens worden de punten voor 3 vakken gevraagd, waarna het gemiddelde wordt teruggegeven.

Technische analyse

Maak een nieuwe klasse Student. Deze klasse heeft 6 properties. Leeftijd en de punten stel je voor met full properties. Een student kan nooit ouder zijn dan 120. Je kan ook nooit een cijfer boven 20 behalen. Over leeftijden en cijfers onder 0 hoef je je geen zorgen te maken, want de achterliggende variabelen zijn bytes en die zijn altijd minstens 0.

  • Name (string)

  • Age (byte)

  • ClassGroup (maak hiervoor een enum ClassGroups)

  • MarkCommunication (byte)

  • MarkProgrammingPrinciples (byte)

  • MarkWebTech (byte)

Voeg aan de klasse een read-only property OverallMark toe. Deze berekent het gemiddelde van de 3 punten als double.

Voeg aan de klasse ook de methode ShowOverview() toe. Deze methode zal een volledig rapport van de student tonen (inclusief het gemiddelde m.b.v. de OverallMark-property).

Test je programma door enkele studenten aan te maken en in te stellen. Volgende statische methode Main zet je in de klasse Student.

Student student1= new Student();
student1.Class = ClassGroups.EA2;
student1.Age = 21;
student1.Name = "Joske Vermeulen";
student1.MarkCommunication = 12;
student1.MarkProgrammingPrinciples = 15;
student1.MarkWebTech = 13;
student1.ShowOverview();

Voorbeeldinteractie(s)

Joske Vermeulen, 21 jaar
Klas: EA2

Cijferrapport:
**********
Communicatie:             12
Programming Principles:   15
Web Technology:           13
Gemiddelde:               13.3

Oefening: H8-RapportModule-V3

Leerdoelen

  • werken met klassen en objecten

  • gebruik maken van properties om geldige waarden af te dwingen

Functionele analyse

Deze is gelijkaardig aan de vorige versie van het programma, maar gebruikt iets geavanceerdere technieken.

Technische analyse

Maak een nieuwe versie van H8-RapportModule-V2, waarbij je een full property gebruikt voor het percentage. Zorg ervoor dat dit steeds tussen 0 en 100 ligt. Vervang ook ComputeHonors door een read-only property, Honors.