Gå til indholdet

Konstanter

En konstant er en variabel, som tildeles en værdi ved erklæring, og som efterfølgende ikke kan rettes igen.

Information til undervisere
  • Kursisterne skal udover at forstå begrebet “konstanter” også forstå enums i C# (relaterede konstanter)
  • De bør løse opgaven

Simple konstanter

Erklæring af en enkelt konstant sker ligesom erklæring af en almindelig variabel med tilføjelse af const-kodeordet, samt en tildeling:

const int antalMåneder = 12;            
const int juleDag = 24;
const int juleMåned = 12;

const double moms = 0.25;

const string farve = "Rød";

Herefter kan konstanterne benyttes, som var det almindelige variabler, men kan blot ikke tildeles en ny værdi:

const int juleDag = 24;
const int juleMåned = 12;
DateTime jul2019 = new DateTime(2019, juleMåned, juleDag);

const double moms = 0.25;
double prisFørMoms = 200;
double prisEfterMoms = 200 * (1 + moms);

moms = 0.15;    // FEJL – kan ikke kompilere

Indbyggede simple konstanter

Du vil uden tvivl falde over konstanter, som Microsoft stiller til rådighed. Det klassiske eksempel er konstanten for Pi, som kan findes under System.Math:

double radius = 7;
// radius opløftet i anden x PI
double areal = System.Math.Pow(radius, 2) * System.Math.PI;

Når du ser en konstant i Visual Studio, har de et specielt ikon:

Konstant i Visual Studio

Relaterede konstanter

En anden type konstant er en decideret typedefinition, som gør det muligt at gruppere relaterede konstanter. Det gør koden langt mere læsbar, samt nem at både skrive og vedligeholde.

Forestil dig, at du skal skrive kode, der repræsenterer en person med navn, alder og køn:

string navn = "Mikkel";
int alder = 16;
int køn = 0;            // 0 = mand, 1 = kvinde

Bemærk, at køn er en int og dermed i princippet kan have værdier fra -2.1 milliard til +2.1 milliard. Vi vedtager dog, at værdien 0 repræsenterer en mand, og værdien 1 en kvinde. Det er helt ok og fuldt lovligt, men vi skal sørge for, at vi tydeligt dokumenterer hvad 0 og 1 betyder for dem, der skriver og læser koden. Det kan naturligvis gøres i koden som kommentarer, men det bliver hurtigt svært at styre.

Derfor har du mulighed for at definere relaterede konstanter i en typedefinition en gang for alle, og så lade kompileren og Visual Studio (Code) hjælpe med at benytte konstanterne.

Denne type hedder en enum (forkortet fra enumeration) og er en af de typer, du kan definere i C# (enum, class, struct, interface, poster og dele-gate). Når du definerer en enum, skaber du en skabelon for, hvordan instanser skal se ud, og en enum består udelukkende af konstanter med navne og værdier:

enum [navn] [:type]
{
    [Konstantnavn] = værdi,
    [Konstantnavn] = værdi,
    [Konstantnavn] = værdi,
    ...
}

Hvis du skal definere en enum, der indeholder konstanter til kønnet på en person, kan den eksempelvis skrives således:

public enum PersonKøn : int 
{ 
    Mand = 0,
    Kvinde = 1
}
Du behøver dog ikke angive typen (int er default):
public enum PersonKøn
{ 
    Mand = 0,
    Kvinde = 1
}

Og hvis du ikke angiver et nummer, vil kompileren nummerere konstanterne fra 0:

public enum PersonKøn
{ 
    Mand,
    Kvinde
}

Det mest normale er, at definering af typer placeres i en fil for selv og typisk med samme navn:

namespace MinTest
{
    public enum PersonKøn
    {
        Mand = 0,
        Kvinde = 1
    }
}

Denne type kunne således placeres i en fil kaldet personkøn.cs.

Brug af enum

Når du har defineret en enumeration, kan den benyttes på samme måde som en int eller en anden type – så koden fra tidligere:

string navn = "Mikkel";
int alder = 16;
int køn = 0;            // 0 = mand, 1 = kvinde
kan nu erstattes med det langt mere logiske og læsbare:
string navn = "Mikkel";
int alder = 16;
PersonKøn køn = PersonKøn.Mand;

Det er ligeledes langt nemmere at skrive i Visual Studio (Code), fordi den godt ved, at der er tale om en relateret konstant:

Brug af en enum i Visual Studio

Typekonvertering

Hvis du ønsker at tildele en enum-variabel en værdi fra et nummer (eksempelvis et nummer fra en database eller fil), skal du foretage en typekonvertering:

// hvis nummeret er et heltal
int nr1 = 1;
PersonKøn køn1 = (PersonKøn)nr1;

// hvis nummeret er en streng er det lidt mere komplekst
string nr2 = "1";
PersonKøn køn2 = (PersonKøn)Enum.Parse(typeof(PersonKøn), nr2);
Hvis du ønsker nummeret fra en enum-variabel, kan du konvertere til en int:
PersonKøn køn = PersonKøn.Kvinde;
int k = (int)køn; // eller brug Convert.ToInt32

Så du kan godt komme fra tal til enum og retur igen.

Overvejelser om brugen af enums

Enums er understøttet af et heltalstalstype, oftest int, repræsenterer hver enum-medlem en talværdi, hvilket gør enum-typer til et kraftfuldt værktøj for at skabe klar og læsbar kode. Dog, denne forbindelse til heltalstyper fører også til særlige overvejelser, når det kommer til at arbejde med enum-værdier.

Som standard er hvert enum-medlem i C# baseret på int-typen og starter fra 0, medmindre andet er specificeret. For eksempel:

enum Farve { Rød, Grøn, Blå } // Rød = 0, Grøn = 1, Blå = 2

Her får Rød automatisk værdien 0, Grøn værdien 1, og så videre. Det er denne underliggende integer-repræsentation, der gør det muligt at behandle enum-værdier som tal.

Da enum-værdier i bund og grund er integers, er det teknisk muligt at caste en hvilken som helst integer til en given enum-type. Dette kan dog føre til ugyldige enum-værdier, hvis det tal, der castes, ikke svarer til et defineret enum-medlem:

Farve f = (Farve)8; // Ugyldig værdi, da der ikke er et 'Farve' medlem, der svarer til 8

For at håndtere sådanne tilfælde, er det vigtigt at validere enum-værdier, før de bruges:

bool erGyldigFarve = Enum.IsDefined(typeof(Farve), f);
if (!erGyldigFarve)
{
    // Håndter den ugyldige værdi passende
}

Validering af enum-værdier er essentiel for at sikre, at din kode forbliver fejltolerant og nem at forstå. Ugyldige enum-værdier kan føre til fejl og forvirrende resultater, især når de bruges i beslutningsprocesser eller komplekse beregninger. Korrekt validering forhindrer disse problemer og bidrager til mere robust og pålidelig kode.

Indbyggede relaterede konstanter

Microsoft har en del forskellige enums i frameworket, som du kan benytte, som du vil. Her er et par eksempler, men der er mange flere:

// ugedag
System.DayOfWeek dag = DayOfWeek.Saturday;                
// farve til konsol
System.ConsoleColor farve = ConsoleColor.Cyan;            
// drev type
System.IO.DriveType t = System.IO.DriveType.Network;     

Brug af flag i enums

Enums kan også bruges til at repræsentere flag, hvor flere værdier kan kombineres. Dette er nyttigt, når du har en række indstillinger, der kan være aktive eller inaktive samtidig. For at gøre dette skal du bruge Flags-attributten og definere værdierne som potenser af 2 (1, 2, 4, 8, osv.) - altså en binær repræsentation.

Her er et eksempel. Antag, at du arbejder med en applikation, der repræsenterer ting i forskellige farver, og du vil kunne vælge en eller flere farver på samme tid.

[Flags]
public enum Farver
{
    Ingen = 0,
    Rød = 1,
    Grøn = 2,
    Blå = 4,
    Gul = 8
}

I dette eksempel repræsenterer hver farve en potens af 2, hvilket gør det muligt at kombinere farverne ved at tilføje deres værdier. For eksempel, hvis du vil repræsentere en ting, der er rød:

Farver minTing = Farver.Rød;

Eller en der er både rød og blå - det kan du gøre det ved at kombinere disse to farver med bitwise OR-operatoren (|):

Farver minTing = Farver.Rød | Farver.Blå;

For at tjekke, om en bestemt farve er inkluderet i en kombination, kan du bruge bitwise AND-operatoren (&):

if ((minTing & Farver.Rød) == Farver.Rød)
{
    Console.WriteLine("Min ting er rød!");
}

På samme måde kan du tilføje eller fjerne farver fra en kombination ved at bruge bitwise OR- og XOR-operatoren:

// Tilføj grøn farve
minTing |= Farver.Grøn;

// Fjern rød farve
minTing ^= Farver.Rød;

Bemærk iøvrigt at Flags-attributten ikke er nødvendig for at bruge flags. Det er blot en hjælp til at gøre det mere læsbart og forståeligt for andre, der læser din kode - og så giver ToString-metoden en pæn repræsentation af værdierne.

Farver minTing = Farver.Rød | Farver.Blå;
Console.WriteLine(minTing); // Udskriver "Rød, Blå"

Brugen af flags i enums giver dig mulighed for at arbejde med kombinationer af værdier på en effektiv og letlæselig måde, hvilket er nyttigt i mange scenarier, hvor du har brug for at repræsentere flere indstillinger eller tilstande samtidig.

Info

Jeg bliver ofte spurgt hvorfor det er nødvendigt at lære det binære/hexadecimale talsystem. Dette er et godt eksempel på, hvorfor det er vigtigt. Hvis du ikke forstår, hvordan binære tal fungerer, vil det være meget svært at forstå, hvordan flags i enums fungerer.

Rød = 1 = 0001
Grøn = 2 = 0010
Blå = 4 = 0100
Gul = 8 = 1000

Kombinationen af rød og blå:

Rød = 0001
Blå = 0100
----------
OR  = 0101 = 5

For at tjekke om en farve er inkluderet i en kombination, bruger du bitwise AND-operatoren:

Min ting = 0101
Rød      = 0001
---------------
AND      = 0001 = 1

For at tilføje en farve til en kombination, bruger du bitwise OR-operatoren:

Min ting = 0101
Grøn     = 0010
---------------
OR       = 0111 = 7

For at fjerne en farve fra en kombination, bruger du bitwise XOR-operatoren:

Min ting = 0111
Rød      = 0001
---------------
XOR      = 0110 = 6

Det er faktisk ret simpelt, når du først forstår det binære talsystem.

Opgaver

  • Prøv selv at lege med simple konstanter i en tom konsol applikation
  • Se N122 Brug af enums