Beetje Coderen

Les 2

C# in een notendop voor Python-kenners

De belangrijkste C#-syntax naast Python: typen, klassen en collecties.

⏱ 45–60 minuten leestijd + oefenen

🎯 Na deze les kun je

  • Python-code lezen en vertalen naar C#
  • de .NET CLI gebruiken
  • List en Dictionary gebruiken

Je kunt programmeren in Python. Goed nieuws: bijna alles wat je weet, werkt in C# ook. Variabelen, if, lussen, functies, lijsten en dictionaries bestaan allemaal, alleen ziet de schrijfwijze er anders uit. Het grootste verschil is dat C# statisch getypeerd is: elke variabele heeft een vast type, en de compiler controleert dat voordat je programma draait. In deze les vertaal je Python-code naar C#, en leer je de dotnet-opdrachtregel kennen.

.NET installeren en de CLI

C# draait op .NET, een gratis platform van Microsoft voor Windows, macOS en Linux. Installeer de .NET 10 SDK (de huidige LTS-versie, long-term support) via dot.net. Controleer daarna:

dotnet --version

Je krijgt iets als 10.0.100. Alles wat je met .NET doet, loopt via het commando dotnet:

Commando Doet
dotnet new console -n Kassa nieuw console-project in de map Kassa
dotnet run bouwen en uitvoeren
dotnet build alleen bouwen (fouten zoeken)
dotnet run kassa.cs één los .cs-bestand uitvoeren, zonder project (sinds .NET 10)
dotnet add package <naam> een pakket van NuGet toevoegen (zoals pip install)

Voor snelle experimenten is dotnet run kassa.cs handig. Voor alles wat groter wordt maak je een project:

dotnet new console -n Kassa
cd Kassa
dotnet run
Hello, World!

In de map staan Program.cs (je code) en Kassa.csproj (de projectinstellingen, zoals de .NET-versie). Als editor kun je Visual Studio Code met de C# Dev Kit gebruiken, of JetBrains Rider.

Hello, World naast elkaar

naam = "Sanne"
print(f"Hallo {naam}!")
string naam = "Sanne";
Console.WriteLine($"Hallo {naam}!");

De verschillen die je meteen ziet:

  • Elke opdracht eindigt met een puntkomma.
  • De variabele heeft een type: string.
  • print heet Console.WriteLine; een f-string heet hier een interpolated string en begint met $.

Typen

Python C# Opmerking
int int 32 bits; voor heel grote getallen long
float double voor metingen en berekeningen
— decimal voor geld: rekent exact in tientallen, achtervoegsel m
str string altijd met dubbele aanhalingstekens
— char één teken, met enkele aanhalingstekens: 'a'
bool bool true / false, kleine letters
None null

var

Je hoeft het type niet altijd uit te schrijven. Met var leidt de compiler het af uit de waarde:

var aantal = 3;            // int
var prijs = 3.45m;         // decimal
var winkel = "De Korrel";  // string

De variabele is nog steeds statisch getypeerd: aantal = "drie"; geeft een compileerfout. var scheelt alleen typen.

Getallen: let op deling en geld

Voorspel wat dit afdrukt:

Console.WriteLine(7 / 2);
Console.WriteLine(7 / 2.0);
Console.WriteLine(0.1 + 0.2);
Console.WriteLine(0.1m + 0.2m);
3
3.5
0.30000000000000004
0.3
  • Twee ints delen geeft een int: het deel achter de komma valt weg. In Python 3 geeft 7 / 2 juist 3.5; daar is // de gehele deling.
  • double heeft dezelfde afrondingsfoutjes als float in Python. Gebruik voor bedragen altijd decimal.

Strings

string product = "speltbrood";
Console.WriteLine(product.Length);          // 10, een eigenschap, geen len()
Console.WriteLine(product.ToUpper());       // SPELTBROOD
Console.WriteLine(product.Contains("brood")); // True
Console.WriteLine($"{3.456m:F2}");          // 3.46 (of 3,46, zie hieronder)

Methoden beginnen in C# met een hoofdletter (ToUpper, niet upper). Achter een dubbele punt in {...} zet je een opmaak: F2 is twee decimalen, C is valuta.

Getallen worden opgemaakt volgens de taalinstelling van de computer. Op een Nederlandse Windows-pc geeft {3.46m:F2} 3,46, op een Linux-server vaak 3.46. Bij invoer werkt het net zo: decimal.Parse("3,45") hangt af van die instelling. In een webapp stel je de cultuur daarom expliciet in; dat zie je later.

Voorwaarden en lussen

if totaal >= 25:
    bezorging = 0
elif totaal >= 10:
    bezorging = 2.5
else:
    bezorging = 3.95

for i in range(3):
    print(i)

for brood in broden:
    print(brood)
decimal bezorging;
if (totaal >= 25)
{
    bezorging = 0;
}
else if (totaal >= 10)
{
    bezorging = 2.5m;
}
else
{
    bezorging = 3.95m;
}

for (int i = 0; i < 3; i++)
{
    Console.WriteLine(i);
}

foreach (var brood in broden)
{
    Console.WriteLine(brood);
}
  • Voorwaarden staan tussen haakjes; blokken staan tussen accolades. Inspringen is alleen voor de leesbaarheid.
  • elif heet else if.
  • for (start; voorwaarde; stap) is de klassieke teller; foreach loopt over een verzameling, zoals Pythons for ... in.
  • and, or, not zijn &&, ||, !.

Functies (methoden)

def bereken_korting(bedrag, percentage=10):
    return bedrag * percentage / 100
static decimal BerekenKorting(decimal bedrag, decimal percentage = 10)
{
    return bedrag * percentage / 100;
}

// korter, voor een functie met één expressie:
static decimal BerekenKorting2(decimal bedrag, decimal percentage = 10) => bedrag * percentage / 100;
  • Het teruggeeftype staat vooraan (decimal); geeft de functie niets terug, dan is dat void.
  • Elke parameter heeft een type.
  • Namen schrijf je in PascalCase (BerekenKorting), lokale variabelen in camelCase (regelTotaal). Python gebruikt snake_case; in C# zie je dat bijna nooit.
  • In een los bestand mag de functie onderaan staan; static betekent hier dat hij niet bij een object hoort. Klassen komen in les 06 en verder vanzelf aan bod.

List en Dictionary

broden = ["spelt", "rogge"]
broden.append("wit")
print(len(broden), broden[0])

prijzen = {"spelt": 3.45, "rogge": 3.10}
prijzen["wit"] = 2.50
if "spelt" in prijzen:
    print(prijzen["spelt"])
var broden = new List<string> { "spelt", "rogge" };
broden.Add("wit");
Console.WriteLine($"{broden.Count} {broden[0]}");

var prijzen = new Dictionary<string, decimal>
{
    ["spelt"] = 3.45m,
    ["rogge"] = 3.10m,
};
prijzen["wit"] = 2.50m;
if (prijzen.TryGetValue("spelt", out var prijs))
{
    Console.WriteLine(prijs);
}
  • List<string> lees je als "lijst van strings". Tussen < > staat het type van de elementen: een lijst bevat altijd één soort.
  • Count in plaats van len().
  • prijzen["mais"] voor een sleutel die niet bestaat, gooit een KeyNotFoundException (zoals KeyError). TryGetValue controleert en leest in één keer; ContainsKey controleert alleen.

null en nullable types

string? naam = null;
Console.WriteLine(naam?.Length ?? 0);   // 0

Moderne C#-projecten staan nullable reference types aan. Een string mag dan geen null zijn; een string? wel. Wil je iets met een string? doen, dan waarschuwt de compiler als je niet eerst controleert. Twee handige operatoren:

  • naam?.Length: als naam null is, is het resultaat ook null, in plaats van een crash.
  • x ?? 0: als x null is, neem dan 0.

Uitgewerkt voorbeeld: een kassabon

var producten = new Dictionary<string, decimal>
{
    ["speltbrood"] = 3.45m,
    ["croissant"] = 1.20m,
    ["appeltaart"] = 12.50m,
};

var bestelling = new List<(string Product, int Aantal)>
{
    ("speltbrood", 2),
    ("croissant", 4),
    ("appeltaart", 1),
};

decimal totaal = 0;
foreach (var (product, aantal) in bestelling)
{
    decimal regelTotaal = producten[product] * aantal;
    totaal += regelTotaal;
    Console.WriteLine($"{aantal,2} x {product,-12} {regelTotaal,8:F2}");
}

Console.WriteLine(new string('-', 26));
Console.WriteLine($"{"Totaal",-17} {totaal,8:F2}");

decimal btw = Math.Round(totaal * 9 / 109, 2);
Console.WriteLine($"{"waarvan btw 9%",-17} {btw,8:F2}");
 2 x speltbrood       6.90
 4 x croissant        4.80
 1 x appeltaart      12.50
--------------------------
Totaal               24.20
waarvan btw 9%        2.00

Nieuwe dingen:

  • (string Product, int Aantal) is een tuple met namen, net als een tuple in Python. foreach (var (product, aantal) in ...) pakt hem uit.
  • {aantal,2} maakt een kolom van 2 tekens breed, rechts uitgelijnd; {product,-12} een kolom van 12, links uitgelijnd (min-teken).
  • De btw zit in de prijs: 9/109 van het totaal is btw.

Typische fouten

Melding Oorzaak
; expected Puntkomma vergeten
Cannot implicitly convert type 'double' to 'decimal' 3.45 zonder m in een decimal-berekening
The name 'x' does not exist in the current context Typfout, of variabele binnen een { }-blok gemaakt en daarbuiten gebruikt
KeyNotFoundException Sleutel bestaat niet; gebruik TryGetValue
Uitkomst 3 in plaats van 3,5 Twee ints gedeeld

Oefeningen

Oefening 1. Vertaal naar C#:

def is_even(n):
    return n % 2 == 0

for n in range(1, 6):
    if is_even(n):
        print(f"{n} is even")
Modelantwoord oefening 1
for (int n = 1; n < 6; n++)
{
    if (IsEven(n))
    {
        Console.WriteLine($"{n} is even");
    }
}

static bool IsEven(int n) => n % 2 == 0;

Oefening 2. Wat is het type van x in var x = 10 / 4; en wat is de waarde?

Modelantwoord oefening 2

int, met waarde 2. Voor 2,5 schrijf je 10 / 4.0 (double) of 10m / 4 (decimal).

Oefening 3. Maak een Dictionary<string, int> met voorraad en schrijf een functie Verkoop(voorraad, product) die de voorraad met één verlaagt, of false teruggeeft als het product er niet (meer) is.

Modelantwoord oefening 3
static bool Verkoop(Dictionary<string, int> voorraad, string product)
{
    if (!voorraad.TryGetValue(product, out var aantal) || aantal == 0)
    {
        return false;
    }
    voorraad[product] = aantal - 1;
    return true;
}

Lab: kassabon als console-app

  1. Maak een console-project Kassa met dotnet new console.
  2. Leg producten en prijzen vast in een Dictionary<string, decimal>.
  3. Vraag met Console.ReadLine() om producten en aantallen tot de gebruiker een lege regel invoert; meld onbekende producten netjes.
  4. Druk een kassabon af met uitgelijnde kolommen, totaal en het btw-bedrag (9%).
  5. beetje check aspnet-les2

Samenvatting

  • C# is statisch getypeerd: elke variabele heeft een type; var laat de compiler het afleiden.
  • Puntkomma's, accolades, haakjes om voorwaarden; methoden in PascalCase.
  • decimal (met m) voor geld; int / int rondt naar beneden af.
  • List<T> en Dictionary<TKey, TValue> zijn de tegenhangers van list en dict; TryGetValue voorkomt crashes.
  • dotnet new, dotnet run en dotnet add package zijn de basiscommando's.

Volgende les: LINQ, de manier om in C# met verzamelingen te werken.

Lab

Kassabon als console-app

💻 Dit lab doe je op je eigen computer met Docker. Start de lab-container met (geeft de opdracht een eigen startcommando, gebruik dan dat):

$ docker run -it --rm registry.wizg.it/webhosting/beetje-labs:aspnet-les2

Volg daarna de stappen van de opdracht hierboven. Nog geen Docker? Zie les 14 van Een beetje Linux & Docker. Voor dit lab is er geen live lab in de browser.

Klaar? Controleer je werk met:

$ beetje check aspnet-les2

📝 Quiz: C# voor Python-kenners

5 vragen · 70% nodig om te slagen

1. Welk type gebruik je in C# voor geldbedragen?
2. Wat geeft 7 / 2 in C#?
3. Wat betekent var in C#?
4. Hoe lees je veilig een waarde uit een Dictionary als de sleutel misschien niet bestaat?
5. Wat doet het commando dotnet new console -n Kassa?