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.
Variabelen en het static keyword
Zonder het keyword heeft ieder object z'n eigen variabelen en aanpassingen binnen het object aan die variabelen heeft geen invloed op andere objecten. We tonen eerst de werking zoals we gewend zijn en vervolgens hoe static werkt.
Variabelen ZONDER static
Gegeven volgende klasse:
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.
Variabelen MET static
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.
Methoden met static
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.
Voorbeeld van static methoden
Stel dat we enkele veelgebruikte methoden willen groeperen en deze gebruiken zonder telkens een object te moeten aanmaken dan doen we dit als volgt:
We kunnen deze methoden nu als volgt aanroepen:
Mooi toch.
Nog een voorbeeld
In het volgende voorbeeld gebruiken we een static variabele om bij te houden hoeveel objecten (via de constructor) er van de klasse reeds zijn aangemaakt:
Merk op dat we de methoden VerminderFiets enkel via de klasse kunnen aanroepen:
private static?
Merk op dat, in bovenstaand voorbeeld, aantalFietsenprivate is en dat er geen publieke property aanwezig is, terwijl een constructor wel bij een instantie hoort. Herinner je ook dat private onderdelen van een klasse alleen toegankelijk zijn vanuit code binnenin die klasse. De code voor objecten van een bepaalde klasse staat ook binnenin die klasse, dus private static onderdelen van een klasse zijn toegankelijk voor de objecten van die klasse!
Static vs non-static
Van zodra je een methode hebt die static is dan zal deze methode enkel andere `static methoden en variabelen kunnen aanspreken. Dat is logisch: een static methode heeft geen toegang tot de gewone niet-statische variabelen van een individueel object, want welk object zou hij dan moeten aanpassen?
Volgende code zal dus een error geven:
De error die verschijnt An object reference is required for the non-static field, method, or property 'Program.Fiets.gewicht' zal bij de lijn gewicht-- staan.
Een eenvoudige regel is te onthouden dat van zodra je in een static omgeving (meestal een methode) bent je niet meer naar de niet-static delen van je code zal geraken.
Static en main
Dit verklaart ook waarom je bij console applicaties in Program.cs steeds alle methoden static moet maken. Een console-applicatie is als volgt beschreven wanneer je het aanmaakt:
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.
Constructors
Constructors
Werking new operator
Objecten die je aanmaakt komen niet zomaar tot leven. Nieuwe objecten maken we aan met behulp van de
Spelen met strings
We gebruiken het einde van klassen en objecten om iets dieper in de String klasse te duiken en aan te tonen dat er tal van nuttige zaken bestaan om met strings te werken.
Verbatim character
Door een @ (verbatim character) voor een string te plaatsen zeggen we concreet: "de hele string die nu volgt moet je beschouwen zoals hij er staat. Je mag alle escape karakters negeren.
H10: Geavanceerde klassen en objecten
Dit wordt vaak gebruikt om een filepath iets leesbaarder te maken.
Zonder verbatim: string path= "c:\\Temp\\myfile.txt";
Met verbatim: string path= @"c:\Temp\myfile.txt";
Splitsen en samenvoegen
Split
Aan het einde van dit hoofdstuk willen we een csv-bestand (comma separated value) splitsen. De Split methode laat toe een string te splitsen op een bepaald teken. Het resultaat is steeds een array van strings.
Uiteraard kan je dit dus gebruiken om op eender welk char te splitsen en dus nit enkel een ',' (komma).
Join
Via Join kunnen we array van string terug samenvoegen. Het resultaat is een nieuwe string.
Volgende voorbeeld zal de eerder array van het vorige voorbeeld opnieuw samenvoegen maar nu met telkens een ; tussen iedere string:
Voorgaande methoden zijn static en moet je dus via de klasse String doen en niet via de objecten zelf.
Andere nuttige methoden
Volgende methoden/properties kan je rechtstreeks op de string-objecten aanroepen (i.e. niet static methoden)
int Length: geeft totaal aantal karakters in de string
IndexOf(string para): geeft int terug die de index bevat waar de string die je als parameter meegaf begint
Trim(): verwijdersd alle onnodige spaties vooraan en achteraan de string en geeft de opgekuiste string terug
ToUpper(): zal de meegegeven string naar ALLCAPS omzetten en geeft de nieuwe string als resultaat terug
ToLower(): idem maar dan naar kleine letters
Replace(string old, string news): zal in de string alle substring die gelijk zijn aan old vervangen door de meegegeven news string
Strings vergelijken
De correcte manier om strings te vergelijken is met de Compare(string s1, string s2) methode. Deze zal een int terug geven:
-1 : de string s1 komt voor de strings2 indien je ze lexicografisch zou sorteren (dit is ongeveer hetzelfde als alfabetisch, maar maakt bijvoorbeeld een onderscheid tussen kleine letters en hoofdletters).
0: beide strings zijn identiek
1: de string s2 komt voor s1
Tekst files uitlezen
De System.IO namespace bevat tal van nuttige methoden en klassen om met bestanden te werken. Om gebruik hiervan te maken plaats je bovenaan je file:
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.
CSV uitlezen
Dankzij ReadAllLines en Split hebben we nu alle bouwstenen om eenvoudig een csv-bestand te verwerken.
Stel je voor dat een bestand soccer.csv volgende tekst bevat:
Volgende code zal dit bestand uitlezen en de individuele data op het scherm tonen:
CSV downloaden
Vaak zal je een online bestand willen verwerken. De WebClient klasse heeft tal van manieren om met online bronnen te werken. Deze klasse bevindt zich in de System.Net namespace en vereist dus dat je bovenaan je code volgende lijn toevoegt:
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.
CSV wegschrijven
Je kan tekst uit een bestand lezen, maar uiteraard kan je ook naar een bestand wegschrijven. De 2 eenvoudigste manieren zijn:
File.WriteAllText: deze gebruik je als je 1 enkele string wilt wegschrijven
File.WriteAllLines: deze is de omgekeerde van ReadAllLines() en zal een array van strings wegschrijven.
Een voorbeeld:
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:
class Mens
{
private int leeftijd=1;
public void Jarig()
{
leeftijd++;
}
public void ToonLeeftijd()
{
Console.WriteLine(leeftijd);
}
}
Mens m1= new Mens();
Mens m2= new Mens();
m1.Jarig();
m1.Jarig();
m2.Jarig();
m1.ToonLeeftijd();
m2.ToonLeeftijd();
3
2
class Mens
{
private static int leeftijd=1;
public void Jarig()
{
leeftijd++;
}
public void ToonLeeftijd()
{
Console.WriteLine(leeftijd);
}
}
Mens m1= new Mens();
Mens m2= new Mens();
m1.Jarig();
m1.Jarig();
m2.Jarig();
m1.ToonLeeftijd();
m2.ToonLeeftijd();
4
4
Math.Pow(3,2);
Math myMath= new Math(); //dit mag niet!
myMath.Pow(3,2)
class EpicLibray
{
static public void ToonInfo()
{
Console.WriteLine("Ik ben ik");
}
static public int TelOp(int a, int b)
{
return a+b;
}
}
EpicLibrary.ToonInfo();
int opgeteld= EpicLibrary.TelOp(3,5);
class Fiets
{
private static int aantalFietsen = 0;
public Fiets()
{
aantalFietsen++;
Console.WriteLine($"Er zijn nu {aantalFietsen} gemaakt");
}
public static void VerminderFiets()
{
aantalFietsen--;
}
}
Fiets.VerminderFiets();
class Mens
{
private int gewicht=50;
private static void VerminderGewicht()
{
gewicht--;
}
}
public class Program
{
public static void Main()
{
}
}
string[] namen = { "Tim", "Jos", "Mo" };
int[] leeftijden = { 34, 76, 23 };
string[] lines = new string[namen.Length];
for (int i = 0; i < lines.Length; i++)
{
lines[i] = $"{i},{namen[i]},{leeftijden[i]}";
}
System.IO.File.WriteAllLines("ages.csv", lines);
new
operator zoals we al gezien hebben:
De new operator doet 2 dingen:
Het maakt een object aan in het geheugen
Het roept de operator van het object aan voor eventuele extra initialisatie
Via de constructor van een klasse kunnen we extra code meegeven die moet uitgevoerd worden telkens een nieuw object van dit type wordt aangemaakt.
De constructor is een unieke methode die wordt aangeroepen bij het aanmaken van een object, daarom dat we ronde haakjes zetten bij new Student().
Soorten constructors
Als programmeur van eigen klassen zijn er 3 opties voor je:
Je gebruikt geen constructors: het leven gaat voort zoals het is. Je kunt objecten aanmaken zoals eerder getoond.
Je hebt enkel een default constructor nodig. Je kan nog steeds objecten met new Student() aanmaken, maar je gaat zelf beschrijven wat er moet gebeuren bij de default constructor
Je wenst gebruik te maken van een of meerdere overloaded constructoren, hierbij zal je dan extra argumenten kunnen meegeven bij de creatie van een object, bijvoorbeeld: new Student(24, "Jos").
Constructors zijn soms gratis, soms niet
Een lege default constructor voor je klasse krijg je standaard wanneer je een nieuwe klasse aanmaakt. Je ziet deze niet en kan deze niet aanpassen. Je kan echter daarom altijd objecten met new myClass() aanmaken.
Van zodra je echter beslist om zelf een of meerdere constructors te schrijven zal C# zeggen "ok, jij je zin, nu doe je alles zelf". De default constructor die je gratis kreeg zal ook niet meer bestaan en heb je die dus nodig dan zal je die dus zelf moeten schrijven!
Default constructor
De default constructor is een constructor die geen extra parameters aanvaardt. Een constructor bestaat ALTIJD uit volgende vorm:
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.
Opmerking bij voorgaande code
Als je op een gelijkaardige manier in andere programmeertalen twee of meerdere Studenten snel na mekaar aanmaakt zullen deze dezelfde leeftijd hebben. Dit is omdat ieder object z'n eigen Random aanmaakt en zoals we weten zal een random generator dezelfde getallen genereren als deze vlak na mekaar (in tijd) zijn aangemaakt. Een oplossing zullen we hier later voor zien. Spoiler, static is de oplossing hiervoor:
Constructor met parameter(s)
Soms wil je argumenten aan een object meegeven bij creatie. We willen bijvoorbeeld de leeftijd meegeven die het object moet hebben bij het aanmaken. Met andere woorden, stel dat we dit willen schrijven:
Als we dit met voorgaande klasse , die enkel een default constructor heeft, uitvoeren zal de code een fout geven. C# vindt geen constructor die een int als parameter aanvaardt.
Dat was eenvoudig. Maar denk eraan: je hebt een eigen constructor geschreven en dus heeft C# gezet "ok, je schrijft zelf constructor, trek je plan. Maar de default zal je ook zal moeten schrijven!" Je kan nu enkel je objecten met new Student(25) aanmaken. Schrijf je new Student() dan zal je een error krijgen. Wil je die constructor nog hebben, dan zal je die met de hand moeten schrijven, bijvoorbeeld:
Wanneer heb ik constructoren nodig?
Tot zeer recent maakten we onze objecten steeds aan met de default constructor. Pas daarna gaven we eventuele properties de juiste waarde. Dat houdt een risico in: er is een periode waarin onze objecten nog niet "af" zijn. In het slechtste geval vergeten we zelfs om de properties in te stellen en krijgen we objecten die misschien ongeldig zijn.
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:
Student FrankVermeulen = new Student();
private int leeftijd= 35;
class Student
{
private int age;
}
class Student
{
public Student()
{
// zet hier de code die bij initialisatie moet gebeuren
}
private int age;
}
class Student
{
public Student()
{
Random r= new Random();
age= r.Next(10,20);
}
private int age;
}
class Student
{
static Random r= new Random();
public Student()
{
age= r.Next(10,20);
}
private int age;
}
Student jos= new Student(19);
class Student
{
public Student(int startage)
{
age= startage
}
private int age;
}
class Student
{
public Student(int startage) // met parameter
{
age= startage;
}
public Student() //default
{
Random r= new Random();
age= r.Next(10,20);
}
private int age;
}
class Student
{
public Student(int startage) //overloaded
{
age= startage;
}
public Student(int startage, bool werkstart) //overloaded
{
age= startage;
isWerkStudent= werkstart;
}
public Student() //default
{
Random r= new Random();
age= r.Next(10,20);
}
private int age;
private bool isWerkStudent
}
class Student
{
public Student(int startage) : this(startage, false)
{
// niet nodig hier code uit uitgebreidere constructor te herhalen
// hier kan nog extra code worden uitgevoerd na de oproep van de uitgebreide constructor
}
public Student(int startage, bool werkstart) //overloaded
{
age= startage;
isWerkStudent=werkstart;
}
public Student() //default
{
// helaas wordt onderstaande code pas **na** de chained constructor opgeroepen
// gebruik van this heeft hier dus niet veel zin
Random r= new Random();
age= r.Next(10,20);
}
private int age;
private bool isWerkStudent
}
Schrijf een statische klasse CSVDemo met een statische methode Run(). Deze downloadt automatisch dit bestand en print de info in de eerste drie kolommen op het scherm. Tussen elke kolom verschijnt een tab.
De nieuwe Squirtle heeft maximum 40 HP en heeft momenteel 20 HP.
Waarde van het lotje: 77
Waarde van het lotje: 8
Waarde van het lotje: 12
Waarde van het lotje: 14
Waarde van het lotje: 51
Waarde van het lotje: 97
Waarde van het lotje: 20
Waarde van het lotje: 15
Waarde van het lotje: 32
Waarde van het lotje: 68