Gå til indholdet

Simple variabler

Alle moderne programmeringssprog giver mulighed for at opbevare midlertidige værdier i hukommelsen, mens en applikation afvikles, og typisk kan man få adgang til disse værdier gennem selvvalgte navne. Det kaldes variabler og dækker i virkeligheden over en ret kompleks funktionalitet – herunder hvor og hvordan værdier skal placeres i hukommelsen, sammenhæng mellem variabelnavne og adresser i hukommelsen, hvilken kode har adgang til hvilke variabler, hvordan og hvornår skal der ryddes op, optimering og meget andet.

Information til undervisere
  • Der er ikke mange ting her som ikke er vigtige - det skulle da lige være de sidste afsnit omkring DateTimeOffset og DateOnly/TimeOnly.
  • Det sværeste at forstå er nok typekonvertering - brug eksemplet med et 8 bit tal der skal konverteres til et 4 bit tal. Tallet 10 går fint men tallet 20 duer ikke.

  • Der er nogle opgaver som kursisterne bør kunne.

Men heldigvis skal du i C# ikke bekymre dig om særlig meget i relation til variabler – i hvert fald ikke i den grundlæggende C#. Det klarer kompiler og runtime for dig.

Hukommelse

Når en C# applikation startes, vil den blive tildelt plads i hukommelsen af runtime og efterfølgende af operativsystemet:

Hukommelse

Der vil både blive afsat (allokeret med et fint ord) plads til de binære instruktioner og de midlertidige data. I C# vil du primært befinde dig i data-delen, men som du skal se senere, kan du også skabe referencer til metoder (brug af Delegate-typen), som er placeret i program-delen.

I modsætning til nogle andre programmeringssprog vil du i C# typisk ikke have nogen adgang til hukommelsen, medmindre det sker gennem variabler. I andre sprog kan du tilgå hukommelsen gennem pointere (pegepinde), men det er i grundlæggende C# ikke en mulighed.

C# er jo som tidligere nævnt typestærk og det betyder, at alle variabler skal oprettes (erklæres) af en konkret type. Det sker blandt andet for at runtime ved, hvor meget plads, der skal allokeres, og for at det er nemmere at optimere.

Så når du skriver kode, der anmoder om en variabel, der kan opbevare et heltal, skal du tage stilling til, hvilken type du vil benytte. Den mest benyttede heltalstype i C# er en Int32 (int) og den fylder 32 bit.

Info

Læs helt grundlæggende om variabler her og det binære talsystem her.

Hukommelsen er jo i virkeligheden en masse små registre, som hver kan indeholde 1 bit (og dermed værdien 0 eller 1). Med 8 af disse små registre (også kaldet en byte) kan du opbevare et heltal med værdier fra -127 til 128. På 16 bit kan du opbevare et heltal med værdier fra -32.768 til 32.767. På 32 bit omkring -2.1 milliard til +2.1 milliard, og på 64 bit er der tale om et meget stort negativt eller positivt tal.

Udover typen skal du også tage stilling til det navn, du gerne vil benytte som tilgang til værdien.

Så når du skriver kode som:

int a = 0;
anmoder du runtime om at allokere 32 bit i hukommelsen og gemme værdien 0, og hver gang du benytter variablen a, vil du have adgang til det område i hukommelsen. C# er (som også tidligere nævnt) typesikker, og det betyder, at den allokerede plads, du kan tilgå gennem variablen, er din, og du kan være sikker på, at intet andet ændrer værdien medmindre, du selv giver mulighed for det.

Hvis du skriver kode som:

bool a = false;
double b = 200.23;
DateTime c = new DateTime(2019, 9, 21);

anmoder du runtime om at allokere 8 bit til a (sand/falsk) samt tildele værdien false, allokere 64 bit til b (kommatal) samt tildele værdien 200,23 og allokere 64 bit til c (dato og tid) samt tildele værdien 21/9-2019.

Info

Unsafe

Blot til orientering findes der avancerede muligheder i C# for at tilgå hukommelsen direkte gennem en adresse – men det kræver brug af såkaldt unsafe kode og bruges meget sjældent. Bare navnet unsafe indikerer jo også, at det ikke er noget, du bør bruge ;)

Variabelnavne

Du kan navngive dine variabler, som du har lyst – med nogle få undtagelser:

  • Navnet skal begynde med et bogstav eller en underscore (_)
  • Navnet må ikke kun bestå af underscores
  • Navnet må kun bestå af bogstaver, tal eller underscores
  • Man må gerne benytte Æ, Ø, Å og andre tegn.

De fleste udviklere benytter en navngivningsstandard, så der er en eller anden form for fælles standard, men det er helt op til dig. Jeg vil dog anbefale dig at bruge Microsofts navngivningsstandard , som kan forkortes ned til, at variabler i metoder starter med et lille bogstav, og benytter CamelCasing (hvert ord i en variabel skrives med stort bortset fra det første).

Her er et par eksempler:

a
antal
månederPrÅr
gennemsnitLøn
gadeOgBy
Om du vil kode på dansk eller engelsk er helt op til dig. Du må gerne bruge danske bogstaver, men spørgsmålet er, hvor smart det er. I dit helt eget projekt, og i denne bog, er det ligegyldigt, men i et projekt hvor koden måske skal ses af udlændinge, er danske bogstaver eller japanske tegn måske ikke så fikst.
int  = 1;
Console.WriteLine();

Men kompileren er altså ligeglad.

Virkefelter

C# er et rigtigt semikolon- og tuborgklammesprog, og tuborgklammerne bruges til at definere en blok kode. Det kaldes også et virkefelt (på engelsk scope).

Når du erklærer variabler i en metode, kan du antage, at de lever og kan tilgås i denne metode – samt eventuelle indre virkefelter (andre tuborgklammer). Kompileren skal nok sørge for at blokere adgang andre steder fra.

Derfor er det vigtigt, at du har styr på tuborgklammerne. De definerer et område i koden, hvor variabler lever.

Se følgende kode:

bool a = true;
if (a == true)
{
    // Et virkefelt       
}

for (int i = 0; i < 10; i++)
{
    // Et virkefelt
}

{
    // Et virkefelt
}

void Test()
{
    // Et virkefelt
    { 
        // Et indre virkefelt
    }
}
Variabler kan kun tilgås i samme eller indre virkefelter:

int a = 1;
// her kan a tilgås
{
    int b = 2;
    // her kan a og b tilgås
    {
        int c = 3;
        // her kan a, b og c tilgås
    }
    // her kan a og b tilgås
}
// her kan a tilgås
Hvis du forsøger at få fat på en variabel uden for et virkefelt, vil Visual Studio og kompileren ikke give dig lov.

Det står dig jo frit for at benytte tabuleringer, men som du kan se, giver det et godt overblik over kode og virkefelter. Du kan selv formatere koden, men du vil kunne spare tid ved at lade Visual Studio gøre det. Forudsat at der ikke er nogen fejl i koden, kan du bruge Format document fra Advanced-menuen under Edit-menuen. Genvejstasten er Ctrl+E+D eller Ctrl+K+D i Visual Studio. I Visual Studio Code kan du trykke F1 og skrive format.

Heltal

Der findes især fire forskellige typer, du kan bruge til at skabe heltal i C#. De kan enten tilgås gennem et reelt typenavn eller et genvejsnavn, som de fleste benytter:

Typenavn Genvej Forklaring Spænd
System.Byte byte 8-bit uden fortegn 0-255
System.Int16 short 16-bit med fortegn -32.768 til 32.767
System.Int32 int 32-bit med fortegn -2.147.483.648 til 2.147.483.647
System.Int64 long 64-bit med fortegn meget lille tal til meget stort tal

Når du skriver en heltalskonstant (altså blot et tal) i koden vil kompileren opfatte det som en int, og i virkeligheden vil du sjældent bruge de andre variabeltyper.

Du undrer dig måske over, at du ikke skal spare så meget plads som muligt og eksempelvis bruge en byte, hvis du blot skal tælle til 10. Men du skal mere kigge på den CPU, som applikationen afvikles på. Hvis der er tale om en 32 bit eller 64 bit arkitektur giver det ikke den store (om nogen) forskel at flytte 32 bit rundt i stedet for 8 bit. Med moderne maskiner kan man faktisk undre sig over, at det ikke er et 64 bit heltal (long) som er default i stedet for et 32 bit heltal (int). Men det skyldes historik og kompatibilitet.

Hvis du vil erklære en variabel af typen int, kan du gøre det på flere måder. Det nemmeste at forstå, og det mest pædagogiske, er således:

System.Int32 a = new System.Int32();
Koden er logisk og pædagogisk, fordi det er tydeligt, at du fortæller kompileren, at du ønsker en variabel af typen System.Int32 kaldet a, og derefter skaber en ny instans af denne type og gemmer den i variablen. Variablen a vil blive tildelt en defaultværdi på 0.

Samme kode kunne skrives ved brug af int, da int er genvejsnavnet til System.Int32:

int b = new int();

Det giver præcis samme resultat – skab variabel kaldet b af typen System.Int32, skab en ny instans og gem værdien. Variablen b vil også blive tildelt en defaultværdi på 0.

Du må gerne skrive kode således, men Microsoft har givet mulighed for, at man kan skabe variabler af de fleste strukturer på en nemmere måde – uden brug af new:

System.Int32 a;
int b;
Det er ikke helt så pædagogisk, men nemt og giver samme resultat dog med den forskel, at både a og b ikke er initialiserede, og skal tildeles en værdi inden brug.

Men prøv at læse koden:

int a;

som

System.Int32 a = new System.Int32();

og tænk: erklær en variabel af typen System.Int32 kaldet a, skab en ny (new) instans og bind variablen og instansen sammen.

Du kan dog vælge at initialisere variablen sammen med erklæringen:

System.Int32 a = 0;
int b = 0;
De fleste vælger (naturligvis) at bruge genvejsnavnet og initialisere med det samme:
int a = 1;

Så længe du arbejder med samme type, må du gerne erklære flere variabler i samme instruktion:

int a = 0, b = 0, c = 0, d = 0;
De fire nævnte datatyper er alle strukturer, hvilket har betydning for, hvor værdier placeres i hukommelsen, og hvad der reelt gemmes. Når der er tale om strukturer, kalder man variabler for værdibaserede variabler, fordi der opbevares værdier, og variabler (og dermed værdier) gemmes i et område i hukommelsen, der kaldes en stak. Det kommer vi tilbage til senere – lige nu skal du bare notere, at strukturer er værdibaserede typer.

Tip

Hvis du er i tvivl, om du har fat i en struktur eller en klasse, kan du altid holde musen over datatypen i Visual Studio eller Visual Studio Code. Så fremgår det meget tydeligt, hvilken type der er tale om.

Kommatal

Der findes grundlæggende tre forskellige typer til at håndtere kommatal:

Typenavn Genvej Forklaring Betydende cifre
System.Single float 32-bit reelt tal Omkring 7
System.Double double 64-bit reelt tal Omkring 15
System.Decimal decimal 128-bit reelt tal Omkring 28

Både float og double benytter den såkaldte floating-point standard, som i de fleste programmeringssprog benyttes til at repræsentere reelle tal. De betydende cifre (før eller efter komma) er afhængig af størrelsen, men generelt kan en double indeholde et meget stort (få decimaler) eller meget lille (mange decimaler) tal. Kommatal skrives med punktum, når du benytter konstanter i koden:

float a = 0;
double b = 0;
decimal c = 0;

b = 233.2341;
Typen double er mest benyttet og er også standard i C#, men decimal kan bruges, hvis beregninger kræver en præcis håndtering af decimaler og styring af afrunding. En double kan i nogen situationer være udfordrende grundet den tilhørende algoritme, men til gengæld er den superhurtig sammenlignet med en decimal. Det klassiske eksempel på en double-afrundingsfejl er:
double a = .1 + .1 + .1 + .1 + .1 + .1 + .1 + .1 + .1 + .1;
// a = 0.99999999999999989

Foretager man samme beregning med en decimal, er resultatet 1.

Du behøver i langt de fleste tilfælde ikke være så bevidst om eventuelle afrundingsproblemer på 16. eller 17. decimal og blot benytte en double til de dine kommatal.

Info

Floating-point algoritmer er ikke altid helt præcise, fordi floating-point repræsentationen har en begrænset mængde af præcision og opløsning. Dette skyldes, at de repræsenterer tal ved hjælp af en begrænset mængde af bits (typisk 32 eller 64 bits) og skal balancere mellem at repræsentere både meget store og meget små tal. Som et resultat opstår afrundingsfejl, når tal med mange decimaler eller tal, der ikke kan repræsenteres nøjagtigt i binær form, manipuleres og gemmes. Dette fører til unøjagtigheder og fejl, der kan akkumuleres over tid i beregninger, hvilket kan påvirke det endelige resultat.

Operatorer relateret til tal

Når først du har fat i heltal eller reelle tal, kan du naturligvis foretage diverse beregninger ved hjælp af indbyggede operatorer:

Operator Forklaring
+ Plus
- Minus
* Gange
/ Division
% Modulus (returnerer resten ved en division)
+= Adderer og tildeler værdien af en variabel med en værdi
-= Subtraherer og tildeler værdien af en variabel med en værdi
*= Multiplicerer og tildeler værdien af en variabel med en værdi
/= Dividerer og tildeler værdien af en variabel med en værdi
++ Forøger en variabel med én
-- Formindsker en variabel med én

Her er et par eksempler på brug af operatorerne:

int a = 10; // a = 10            
a = a + 10; // a = 20            
a += 10;    // a = 30

int b = 50; // b = 50
b = b - 10; // b = 40            
b -= 10;    // b = 30

int c = 10; // c = 10            
c = c * 2;  // c = 20            
c *= 2;     // c = 40
c /= 4;     // c = 10

int d = 10; // d = 10            
d++;        // d = 11
d--;        // d = 10
De fleste er logiske og nemme at arbejde med. Det eneste, du skal være opmærksom på, er division. To heltal divideret med hinanden giver et nyt heltal, men hvis det ene er et reelt tal, returneres et reelt tal:

int a = 10;
int b = 3;
int c = a / b;  // c = 3

double d = 3.0;

// fejl - udtryk returnerer en double og passer ikke ind i en int
// int e = a / d;  

double e = a / d;   // e = 3.333333333
Bemærk fejlen! Udtrykket a / d (int/double) returnerer en double, og du kan ikke bare putte et 64-bit (floating point) tal ind i et 32-bit heltal. Det vil kræve en typekonvertering.

Hvis du gerne vil vide lidt mere om tal og operatorer i C#, kan du søge efter info om ”overflow/checked”, som dækker over hvad der sker, når en variabel eksempelvis rammer loftet af sin maksimale værdi. Der findes også mere avancerede datatyper som UInt16, UInt64, store heltal i BigInteger-typen samt avancerede komplekse tal i Complex-typen.

Formatering af tal

Du har tit behov for at gemme eller udskrive et tal i formateret form – med eller uden separator, i et givet antal decimaler og i en given kultur (dansk, tysk, amerikansk med videre).

Flere metoder i C# tager såkaldte formateringstegn som argument, og dem kan du bruge for at slippe for selv at formatere tal. Her er nogle af de vigtigste:

Tegn Forklaring Eksempel (DK)
N (antal decimaler) Brug separator for tusinde N2 = 100.000,00
F (antal decimaler) Brug ikke separator for tusinde F3 = 100000,000
C (antal decimaler) Formater som valuta C2 = 100.000,00 kr.
P (antal decimaler) Formater som procent P4 = 3,1415 %

En af metoderne, der benyttes meget i forbindelse med formatering, er ToString-metoden, som findes på alle typer:

int a = 25123;
double b = 232345.3426;
double c = 0.25;

Console.WriteLine(a.ToString("N2"));    // Udskriver 25.125,00
Console.WriteLine(a.ToString("N3"));    // Udskriver 25.125,000
Console.WriteLine(a.ToString("F1"));    // Udskriver 25125,0
Console.WriteLine(a.ToString("C2"));    // Udskriver 25.125,00 kr.

Console.WriteLine(b.ToString("N2"));    // Udskriver 232.345,34
Console.WriteLine(b.ToString("N3"));    // Udskriver 232.345,343 
Console.WriteLine(b.ToString("F5"));    // Udskriver 232345,34260

Console.WriteLine(c.ToString("P0"));    // 25 %
Console.WriteLine(c.ToString("P2"));    // 25,00 %

Samme metode kan også benyttes til forskellige kulturer:

int a = 25123;
double b = 232345.3426;
double c = 0.25;
System.Globalization.CultureInfo culture = new 
        System.Globalization.CultureInfo("en-US");

Console.WriteLine(a.ToString("N2", culture));    // Udskriver 25,123.00
Console.WriteLine(a.ToString("N3", culture));    // Udskriver 25,123.000
Console.WriteLine(a.ToString("F1", culture));    // Udskriver 25123.0
Console.WriteLine(a.ToString("C2", culture));    // Udskriver $25,123.00 

Console.WriteLine(b.ToString("N2", culture));    // Udskriver 232,345.34
Console.WriteLine(b.ToString("N3", culture));    
// Udskriver 232,345.343 
Console.WriteLine(b.ToString("F5", culture));    
// Udskriver 232345.34260

Console.WriteLine(c.ToString("P0", culture));    // 25%
Console.WriteLine(c.ToString("P2", culture));    // 25,00%

Kultur

Du bør angive en kultur, når du formaterer, for du kan ikke styre, hvilken maskine din applikation afvikles på, og det kan give forskellige udfordringer. Sørg derfor altid for at have følgende kode i starten af en applikation:

System.Threading.Thread.CurrentThread.CurrentCulture 
    = new System.Globalization.CultureInfo("en-US");
eller i en nyere konsol applikation med implicit using
Thread.CurrentThread.CurrentCulture 
    = new System.Globalization.CultureInfo("en-US");

Koden for den danske kultur er ”da-DK”, og du kan finde alle de andre hos Microsoft. Der findes også en del andre formateringstegn, som du ligeledes kan finde i dokumentationen.

Liste over kulture

Her er en liste over mulige kulture i .NET 8

Kulturkode Navn
aa Afar
aa-DJ Afar (Djibouti)
aa-ER Afar (Eritrea)
aa-ET Afar (Ethiopia)
af Afrikaans
af-NA Afrikaans (Namibia)
af-ZA Afrikaans (South Africa)
agq Aghem
agq-CM Aghem (Cameroon)
ak Akan
ak-GH Akan (Ghana)
am Amharic
am-ET Amharic (Ethiopia)
ar Arabic
ar-001 Arabic (World)
ar-AE Arabic (United Arab Emirates)
ar-BH Arabic (Bahrain)
ar-DJ Arabic (Djibouti)
ar-DZ Arabic (Algeria)
ar-EG Arabic (Egypt)
ar-ER Arabic (Eritrea)
ar-IL Arabic (Israel)
ar-IQ Arabic (Iraq)
ar-JO Arabic (Jordan)
ar-KM Arabic (Comoros)
ar-KW Arabic (Kuwait)
ar-LB Arabic (Lebanon)
ar-LY Arabic (Libya)
ar-MA Arabic (Morocco)
ar-MR Arabic (Mauritania)
ar-OM Arabic (Oman)
ar-PS Arabic (Palestinian Authority)
ar-QA Arabic (Qatar)
ar-SA Arabic (Saudi Arabia)
ar-SD Arabic (Sudan)
ar-SO Arabic (Somalia)
ar-SS Arabic (South Sudan)
ar-SY Arabic (Syria)
ar-TD Arabic (Chad)
ar-TN Arabic (Tunisia)
ar-YE Arabic (Yemen)
arn Mapuche
arn-CL Mapuche (Chile)
as Assamese
as-IN Assamese (India)
asa Asu
asa-TZ Asu (Tanzania)
ast Asturian
ast-ES Asturian (Spain)
az Azerbaijani
az-Cyrl Azerbaijani (Cyrillic)
az-Cyrl-AZ Azerbaijani (Cyrillic, Azerbaijan)
az-Latn Azerbaijani (Latin)
az-Latn-AZ Azerbaijani (Latin, Azerbaijan)
ba Bashkir
ba-RU Bashkir (Russia)
bas Basaa
bas-CM Basaa (Cameroon)
be Belarusian
be-BY Belarusian (Belarus)
bem Bemba
bem-ZM Bemba (Zambia)
bez Bena
bez-TZ Bena (Tanzania)
bg Bulgarian
bg-BG Bulgarian (Bulgaria)
bin Edo
bin-NG Edo (Nigeria)
bm Bamanankan
bm-ML Bamanankan (Mali)
bn Bangla
bn-BD Bangla (Bangladesh)
bn-IN Bangla (India)
bo Tibetan
bo-CN Tibetan (China)
bo-IN Tibetan (India)
br Breton
br-FR Breton (France)
brx Bodo
brx-IN Bodo (India)
bs Bosnian
bs-Cyrl Bosnian (Cyrillic)
bs-Cyrl-BA Bosnian (Cyrillic, Bosnia & Herzegovina)
bs-Latn Bosnian (Latin)
bs-Latn-BA Bosnian (Latin, Bosnia & Herzegovina)
byn Blin
byn-ER Blin (Eritrea)
ca Catalan
ca-AD Catalan (Andorra)
ca-ES Catalan (Spain)
ca-FR Catalan (France)
ca-IT Catalan (Italy)
ccp Chakma
ccp-BD Chakma (Bangladesh)
ccp-IN Chakma (India)
ce Chechen
ce-RU Chechen (Russia)
ceb Cebuano
ceb-PH Cebuano (Philippines)
cgg Chiga
cgg-UG Chiga (Uganda)
chr Cherokee
chr-US Cherokee (United States)
ckb Central Kurdish
ckb-IQ Central Kurdish (Iraq)
ckb-IR Central Kurdish (Iran)
co Corsican
co-FR Corsican (France)
cs Czech
cs-CZ Czech (Czechia)
cu Church Slavic
cu-RU Church Slavic (Russia)
cy Welsh
cy-GB Welsh (United Kingdom)
da Danish
da-DK Danish (Denmark)
da-GL Danish (Greenland)
dav Taita
dav-KE Taita (Kenya)
de German
de-AT German (Austria)
de-BE German (Belgium)
de-CH German (Switzerland)
de-DE German (Germany)
de-IT German (Italy)
de-LI German (Liechtenstein)
de-LU German (Luxembourg)
dje Zarma
dje-NE Zarma (Niger)
doi Dogri
doi-IN Dogri (India)
dsb Lower Sorbian
dsb-DE Lower Sorbian (Germany)
dua Duala
dua-CM Duala (Cameroon)
dv Divehi
dv-MV Divehi (Maldives)
dyo Jola-Fonyi
dyo-SN Jola-Fonyi (Senegal)
dz Dzongkha
dz-BT Dzongkha (Bhutan)
ebu Embu
ebu-KE Embu (Kenya)
ee Ewe
ee-GH Ewe (Ghana)
ee-TG Ewe (Togo)
el Greek
el-CY Greek (Cyprus)
el-GR Greek (Greece)
en English
en-001 English (World)
en-029 English (Caribbean)
en-150 English (Europe)
en-AE English (United Arab Emirates)
en-AG English (Antigua & Barbuda)
en-AI English (Anguilla)
en-AS English (American Samoa)
en-AT English (Austria)
en-AU English (Australia)
en-BB English (Barbados)
en-BE English (Belgium)
en-BI English (Burundi)
en-BM English (Bermuda)
en-BS English (Bahamas)
en-BW English (Botswana)
en-BZ English (Belize)
en-CA English (Canada)
en-CC English (Cocos [Keeling] Islands)
en-CH English (Switzerland)
en-CK English (Cook Islands)
en-CM English (Cameroon)
en-CX English (Christmas Island)
en-CY English (Cyprus)
en-DE English (Germany)
en-DK English (Denmark)
en-DM English (Dominica)
en-ER English (Eritrea)
en-FI English (Finland)
en-FJ English (Fiji)
en-FK English (Falkland Islands)
en-FM English (Micronesia)
en-GB English (United Kingdom)
en-GD English (Grenada)
en-GG English (Guernsey)
en-GH English (Ghana)
en-GI English (Gibraltar)
en-GM English (Gambia)
en-GU English (Guam)
en-GY English (Guyana)
en-HK English (Hong Kong SAR)
en-ID English (Indonesia)
en-IE English (Ireland)
en-IL English (Israel)
en-IM English (Isle of Man)
en-IN English (India)
en-IO English (British Indian Ocean Territory)
en-JE English (Jersey)
en-JM English (Jamaica)
en-KE English (Kenya)
en-KI English (Kiribati)
en-KN English (St. Kitts & Nevis)
en-KY English (Cayman Islands)
en-LC English (St. Lucia)
en-LR English (Liberia)
en-LS English (Lesotho)
en-MG English (Madagascar)
en-MH English (Marshall Islands)
en-MO English (Macao SAR)
en-MP English (Northern Mariana Islands)
en-MS English (Montserrat)
en-MT English (Malta)
en-MU English (Mauritius)
en-MW English (Malawi)
en-MY English (Malaysia)
en-NA English (Namibia)
en-NF English (Norfolk Island)
en-NG English (Nigeria)
en-NL English (Netherlands)
en-NR English (Nauru)
en-NU English (Niue)
en-NZ English (New Zealand)
en-PG English (Papua New Guinea)
en-PH English (Philippines)
en-PK English (Pakistan)
en-PN English (Pitcairn Islands)
en-PR English (Puerto Rico)
en-PW English (Palau)
en-RW English (Rwanda)
en-SB English (Solomon Islands)
en-SC English (Seychelles)
en-SD English (Sudan)
en-SE English (Sweden)
en-SG English (Singapore)
en-SH English (St Helena, Ascension, Tristan da Cunha)
en-SI English (Slovenia)
en-SL English (Sierra Leone)
en-SS English (South Sudan)
en-SX English (Sint Maarten)
en-SZ English (Eswatini)
en-TC English (Turks & Caicos Islands)
en-TK English (Tokelau)
en-TO English (Tonga)
en-TT English (Trinidad & Tobago)
en-TV English (Tuvalu)
en-TZ English (Tanzania)
en-UG English (Uganda)
en-UM English (U.S. Outlying Islands)
en-US English (United States)
en-US-POSIX English (United States, Computer)
en-VC English (St. Vincent & Grenadines)
en-VG English (British Virgin Islands)
en-VI English (U.S. Virgin Islands)
en-VU English (Vanuatu)
en-WS English (Samoa)
en-ZA English (South Africa)
en-ZM English (Zambia)
en-ZW English (Zimbabwe)
eo Esperanto
eo-001 Esperanto (World)
es Spanish
es-419 Spanish (Latin America)
es-AR Spanish (Argentina)
es-BO Spanish (Bolivia)
es-BR Spanish (Brazil)
es-BZ Spanish (Belize)
es-CL Spanish (Chile)
es-CO Spanish (Colombia)
es-CR Spanish (Costa Rica)
es-CU Spanish (Cuba)
es-DO Spanish (Dominican Republic)
es-EC Spanish (Ecuador)
es-ES Spanish (Spain)
es-GQ Spanish (Equatorial Guinea)
es-GT Spanish (Guatemala)
es-HN Spanish (Honduras)
es-MX Spanish (Mexico)
es-NI Spanish (Nicaragua)
es-PA Spanish (Panama)
es-PE Spanish (Peru)
es-PH Spanish (Philippines)
es-PR Spanish (Puerto Rico)
es-PY Spanish (Paraguay)
es-SV Spanish (El Salvador)
es-US Spanish (United States)
es-UY Spanish (Uruguay)
es-VE Spanish (Venezuela)
et Estonian
et-EE Estonian (Estonia)
eu Basque
eu-ES Basque (Spain)
ewo Ewondo
ewo-CM Ewondo (Cameroon)
fa Persian
fa-AF Persian (Afghanistan)
fa-IR Persian (Iran)
ff Fulah
ff-Adlm Fulah (Adlam)
ff-Adlm-BF Fulah (Adlam, Burkina Faso)
ff-Adlm-CM Fulah (Adlam, Cameroon)
ff-Adlm-GH Fulah (Adlam, Ghana)
ff-Adlm-GM Fulah (Adlam, Gambia)
ff-Adlm-GN Fulah (Adlam, Guinea)
ff-Adlm-GW Fulah (Adlam, Guinea-Bissau)
ff-Adlm-LR Fulah (Adlam, Liberia)
ff-Adlm-MR Fulah (Adlam, Mauritania)
ff-Adlm-NE Fulah (Adlam, Niger)
ff-Adlm-NG Fulah (Adlam, Nigeria)
ff-Adlm-SL Fulah (Adlam, Sierra Leone)
ff-Adlm-SN Fulah (Adlam, Senegal)
ff-Latn Fulah (Latin)
ff-Latn-BF Fulah (Latin, Burkina Faso)
ff-Latn-CM Fulah (Latin, Cameroon)
ff-Latn-GH Fulah (Latin, Ghana)
ff-Latn-GM Fulah (Latin, Gambia)
ff-Latn-GN Fulah (Latin, Guinea)
ff-Latn-GW Fulah (Latin, Guinea-Bissau)
ff-Latn-LR Fulah (Latin, Liberia)
ff-Latn-MR Fulah (Latin, Mauritania)
ff-Latn-NE Fulah (Latin, Niger)
ff-Latn-NG Fulah (Latin, Nigeria)
ff-Latn-SL Fulah (Latin, Sierra Leone)
ff-Latn-SN Fulah (Latin, Senegal)
fi Finnish
fi-FI Finnish (Finland)
fil Filipino
fil-PH Filipino (Philippines)
fo Faroese
fo-DK Faroese (Denmark)
fo-FO Faroese (Faroe Islands)
fr French
fr-029 French (Caribbean)
fr-BE French (Belgium)
fr-BF French (Burkina Faso)
fr-BI French (Burundi)
fr-BJ French (Benin)
fr-BL French (St. Barthélemy)
fr-CA French (Canada)
fr-CD French (Congo [DRC])
fr-CF French (Central African Republic)
fr-CG French (Congo)
fr-CH French (Switzerland)
fr-CI French (Côte d’Ivoire)
fr-CM French (Cameroon)
fr-DJ French (Djibouti)
fr-DZ French (Algeria)
fr-FR French (France)
fr-GA French (Gabon)
fr-GF French (French Guiana)
fr-GN French (Guinea)
fr-GP French (Guadeloupe)
fr-GQ French (Equatorial Guinea)
fr-HT French (Haiti)
fr-KM French (Comoros)
fr-LU French (Luxembourg)
fr-MA French (Morocco)
fr-MC French (Monaco)
fr-MF French (St. Martin)
fr-MG French (Madagascar)
fr-ML French (Mali)
fr-MQ French (Martinique)
fr-MR French (Mauritania)
fr-MU French (Mauritius)
fr-NC French (New Caledonia)
fr-NE French (Niger)
fr-PF French (French Polynesia)
fr-PM French (St. Pierre & Miquelon)
fr-RE French (Réunion)
fr-RW French (Rwanda)
fr-SC French (Seychelles)
fr-SN French (Senegal)
fr-SY French (Syria)
fr-TD French (Chad)
fr-TG French (Togo)
fr-TN French (Tunisia)
fr-VU French (Vanuatu)
fr-WF French (Wallis & Futuna)
fr-YT French (Mayotte)
fur Friulian
fur-IT Friulian (Italy)
fy Western Frisian
fy-NL Western Frisian (Netherlands)
ga Irish
ga-GB Irish (United Kingdom)
ga-IE Irish (Ireland)
gd Scottish Gaelic
gd-GB Scottish Gaelic (United Kingdom)
gl Galician
gl-ES Galician (Spain)
gn Guarani
gn-PY Guarani (Paraguay)
gsw Swiss German
gsw-CH Swiss German (Switzerland)
gsw-FR Swiss German (France)
gsw-LI Swiss German (Liechtenstein)
gu Gujarati
gu-IN Gujarati (India)
guz Gusii
guz-KE Gusii (Kenya)
gv Manx
gv-IM Manx (Isle of Man)
ha Hausa
ha-GH Hausa (Ghana)
ha-NE Hausa (Niger)
ha-NG Hausa (Nigeria)
haw Hawaiian
haw-US Hawaiian (United States)
he Hebrew
he-IL Hebrew (Israel)
hi Hindi
hi-IN Hindi (India)
hr Croatian
hr-BA Croatian (Bosnia & Herzegovina)
hr-HR Croatian (Croatia)
hsb Upper Sorbian
hsb-DE Upper Sorbian (Germany)
hu Hungarian
hu-HU Hungarian (Hungary)
hy Armenian
hy-AM Armenian (Armenia)
ia Interlingua
ia-001 Interlingua (World)
ibb Ibibio
ibb-NG Ibibio (Nigeria)
id Indonesian
id-ID Indonesian (Indonesia)
ig Igbo
ig-NG Igbo (Nigeria)
ii Yi
ii-CN Yi (China)
is Icelandic
is-IS Icelandic (Iceland)
it Italian
it-CH Italian (Switzerland)
it-IT Italian (Italy)
it-SM Italian (San Marino)
it-VA Italian (Vatican City)
iu Inuktitut
iu-CA Inuktitut (Canada)
iu-Latn Inuktitut (Latin)
iu-Latn-CA Inuktitut (Latin, Canada)
ja Japanese
ja-JP Japanese (Japan)
jgo Ngomba
jgo-CM Ngomba (Cameroon)
jmc Machame
jmc-TZ Machame (Tanzania)
jv Javanese
jv-ID Javanese (Indonesia)
jv-Java Javanese (Javanese)
jv-Java-ID Javanese (Javanese, Indonesia)
ka Georgian
ka-GE Georgian (Georgia)
kab Kabyle
kab-DZ Kabyle (Algeria)
kam Kamba
kam-KE Kamba (Kenya)
kde Makonde
kde-TZ Makonde (Tanzania)
kea Kabuverdianu
kea-CV Kabuverdianu (Cabo Verde)
khq Koyra Chiini
khq-ML Koyra Chiini (Mali)
ki Kikuyu
ki-KE Kikuyu (Kenya)
kk Kazakh
kk-KZ Kazakh (Kazakhstan)
kkj Kako
kkj-CM Kako (Cameroon)
kl Kalaallisut
kl-GL Kalaallisut (Greenland)
kln Kalenjin
kln-KE Kalenjin (Kenya)
km Khmer
km-KH Khmer (Cambodia)
kn Kannada
kn-IN Kannada (India)
ko Korean
ko-KP Korean (North Korea)
ko-KR Korean (Korea)
kok Konkani
kok-IN Konkani (India)
kr Kanuri
kr-Latn Kanuri (Latin)
kr-Latn-NG Kanuri (Latin, Nigeria)
ks Kashmiri
ks-Arab Kashmiri (Arabic)
ks-Arab-IN Kashmiri (Arabic, India)
ks-Deva Kashmiri (Devanagari)
ks-Deva-IN Kashmiri (Devanagari, India)
ksb Shambala
ksb-TZ Shambala (Tanzania)
ksf Bafia
ksf-CM Bafia (Cameroon)
ksh Colognian
ksh-DE Colognian (Germany)
kw Cornish
kw-GB Cornish (United Kingdom)
ky Kyrgyz
ky-KG Kyrgyz (Kyrgyzstan)
la Latin
la-VA Latin (Vatican City)
lag Langi
lag-TZ Langi (Tanzania)
lb Luxembourgish
lb-LU Luxembourgish (Luxembourg)
lg Ganda
lg-UG Ganda (Uganda)
lkt Lakota
lkt-US Lakota (United States)
ln Lingala
ln-AO Lingala (Angola)
ln-CD Lingala (Congo [DRC])
ln-CF Lingala (Central African Republic)
ln-CG Lingala (Congo)
lo Lao
lo-LA Lao (Laos)
lrc Northern Luri
lrc-IQ Northern Luri (Iraq)
lrc-IR Northern Luri (Iran)
lt Lithuanian
lt-LT Lithuanian (Lithuania)
lu Luba-Katanga
lu-CD Luba-Katanga (Congo [DRC])
luo Luo
luo-KE Luo (Kenya)
luy Luyia
luy-KE Luyia (Kenya)
lv Latvian
lv-LV Latvian (Latvia)
mai Maithili
mai-IN Maithili (India)
mas Masai
mas-KE Masai (Kenya)
mas-TZ Masai (Tanzania)
mer Meru
mer-KE Meru (Kenya)
mfe Morisyen
mfe-MU Morisyen (Mauritius)
mg Malagasy
mg-MG Malagasy (Madagascar)
mgh Makhuwa-Meetto
mgh-MZ Makhuwa-Meetto (Mozambique)
mgo Metaʼ
mgo-CM Metaʼ (Cameroon)
mi Maori
mi-NZ Maori (New Zealand)
mk Macedonian
mk-MK Macedonian (North Macedonia)
ml Malayalam
ml-IN Malayalam (India)
mn Mongolian
mn-MN Mongolian (Mongolia)
mn-Mong Mongolian (Mongolian)
mn-Mong-CN Mongolian (Mongolian, China)
mn-Mong-MN Mongolian (Mongolian, Mongolia)
mni Manipuri
mni-Beng Manipuri (Bangla)
mni-Beng-IN Manipuri (Bangla, India)
moh Mohawk
moh-CA Mohawk (Canada)
mr Marathi
mr-IN Marathi (India)
ms Malay
ms-BN Malay (Brunei)
ms-ID Malay (Indonesia)
ms-MY Malay (Malaysia)
ms-SG Malay (Singapore)
mt Maltese
mt-MT Maltese (Malta)
mua Mundang
mua-CM Mundang (Cameroon)
my Burmese
my-MM Burmese (Myanmar)
mzn Mazanderani
mzn-IR Mazanderani (Iran)
naq Nama
naq-NA Nama (Namibia)
nb Norwegian Bokmål
nb-NO Norwegian Bokmål (Norway)
nb-SJ Norwegian Bokmål (Svalbard & Jan Mayen)
nd North Ndebele
nd-ZW North Ndebele (Zimbabwe)
nds Low German
nds-DE Low German (Germany)
nds-NL Low German (Netherlands)
ne Nepali
ne-IN Nepali (India)
ne-NP Nepali (Nepal)
nl Dutch
nl-AW Dutch (Aruba)
nl-BE Dutch (Belgium)
nl-BQ Dutch (Bonaire, Sint Eustatius and Saba)
nl-CW Dutch (Curaçao)
nl-NL Dutch (Netherlands)
nl-SR Dutch (Suriname)
nl-SX Dutch (Sint Maarten)
nmg Kwasio
nmg-CM Kwasio (Cameroon)
nn Norwegian Nynorsk
nn-NO Norwegian Nynorsk (Norway)
nnh Ngiemboon
nnh-CM Ngiemboon (Cameroon)
nqo N’Ko
nqo-GN N’Ko (Guinea)
nr South Ndebele
nr-ZA South Ndebele (South Africa)
nso Sesotho sa Leboa
nso-ZA Sesotho sa Leboa (South Africa)
nus Nuer
nus-SS Nuer (South Sudan)
nyn Nyankole
nyn-UG Nyankole (Uganda)
oc Occitan
oc-FR Occitan (France)
om Oromo
om-ET Oromo (Ethiopia)
om-KE Oromo (Kenya)
or Odia
or-IN Odia (India)
os Ossetic
os-GE Ossetic (Georgia)
os-RU Ossetic (Russia)
pa Punjabi
pa-Arab Punjabi (Arabic)
pa-Arab-PK Punjabi (Arabic, Pakistan)
pa-Guru Punjabi (Gurmukhi)
pa-Guru-IN Punjabi (Gurmukhi, India)
pap Papiamento
pap-029 Papiamento (Caribbean)
pcm Nigerian Pidgin
pcm-NG Nigerian Pidgin (Nigeria)
pl Polish
pl-PL Polish (Poland)
prg Prussian
prg-001 Prussian (World)
ps Pashto
ps-AF Pashto (Afghanistan)
ps-PK Pashto (Pakistan)
pt Portuguese
pt-AO Portuguese (Angola)
pt-BR Portuguese (Brazil)
pt-CH Portuguese (Switzerland)
pt-CV Portuguese (Cabo Verde)
pt-GQ Portuguese (Equatorial Guinea)
pt-GW Portuguese (Guinea-Bissau)
pt-LU Portuguese (Luxembourg)
pt-MO Portuguese (Macao SAR)
pt-MZ Portuguese (Mozambique)
pt-PT Portuguese (Portugal)
pt-ST Portuguese (São Tomé & Príncipe)
pt-TL Portuguese (Timor-Leste)
qu Quechua
qu-BO Quechua (Bolivia)
qu-EC Quechua (Ecuador)
qu-PE Quechua (Peru)
quc Kʼicheʼ
quc-GT Kʼicheʼ (Guatemala)
rm Romansh
rm-CH Romansh (Switzerland)
rn Rundi
rn-BI Rundi (Burundi)
ro Romanian
ro-MD Romanian (Moldova)
ro-RO Romanian (Romania)
rof Rombo
rof-TZ Rombo (Tanzania)
ru Russian
ru-BY Russian (Belarus)
ru-KG Russian (Kyrgyzstan)
ru-KZ Russian (Kazakhstan)
ru-MD Russian (Moldova)
ru-RU Russian (Russia)
ru-UA Russian (Ukraine)
rw Kinyarwanda
rw-RW Kinyarwanda (Rwanda)
rwk Rwa
rwk-TZ Rwa (Tanzania)
sa Sanskrit
sa-IN Sanskrit (India)
sah Sakha
sah-RU Sakha (Russia)
saq Samburu
saq-KE Samburu (Kenya)
sat Santali
sat-Olck Santali (Ol Chiki)
sat-Olck-IN Santali (Ol Chiki, India)
sbp Sangu
sbp-TZ Sangu (Tanzania)
sd Sindhi
sd-Arab Sindhi (Arabic)
sd-Arab-PK Sindhi (Arabic, Pakistan)
sd-Deva Sindhi (Devanagari)
sd-Deva-IN Sindhi (Devanagari, India)
se Northern Sami
se-FI Northern Sami (Finland)
se-NO Northern Sami (Norway)
se-SE Northern Sami (Sweden)
seh Sena
seh-MZ Sena (Mozambique)
ses Koyraboro Senni
ses-ML Koyraboro Senni (Mali)
sg Sango
sg-CF Sango (Central African Republic)
shi Tachelhit
shi-Latn Tachelhit (Latin)
shi-Latn-MA Tachelhit (Latin, Morocco)
shi-Tfng Tachelhit (Tifinagh)
shi-Tfng-MA Tachelhit (Tifinagh, Morocco)
si Sinhala
si-LK Sinhala (Sri Lanka)
sk Slovak
sk-SK Slovak (Slovakia)
sl Slovenian
sl-SI Slovenian (Slovenia)
sma Southern Sami
sma-NO Southern Sami (Norway)
sma-SE Southern Sami (Sweden)
smj Lule Sami
smj-NO Lule Sami (Norway)
smj-SE Lule Sami (Sweden)
smn Inari Sami
smn-FI Inari Sami (Finland)
sms Skolt Sami
sms-FI Skolt Sami (Finland)
sn Shona
sn-ZW Shona (Zimbabwe)
so Somali
so-DJ Somali (Djibouti)
so-ET Somali (Ethiopia)
so-KE Somali (Kenya)
so-SO Somali (Somalia)
sq Albanian
sq-AL Albanian (Albania)
sq-MK Albanian (North Macedonia)
sq-XK Albanian (Kosovo)
sr Serbian
sr-Cyrl Serbian (Cyrillic)
sr-Cyrl-BA Serbian (Cyrillic, Bosnia & Herzegovina)
sr-Cyrl-ME Serbian (Cyrillic, Montenegro)
sr-Cyrl-RS Serbian (Cyrillic, Serbia)
sr-Cyrl-XK Serbian (Cyrillic, Kosovo)
sr-Latn Serbian (Latin)
sr-Latn-BA Serbian (Latin, Bosnia & Herzegovina)
sr-Latn-ME Serbian (Latin, Montenegro)
sr-Latn-RS Serbian (Latin, Serbia)
sr-Latn-XK Serbian (Latin, Kosovo)
ss siSwati
ss-SZ siSwati (Eswatini)
ss-ZA siSwati (South Africa)
ssy Saho
ssy-ER Saho (Eritrea)
st Sesotho
st-LS Sesotho (Lesotho)
st-ZA Sesotho (South Africa)
su Sundanese
su-Latn Sundanese (Latin)
su-Latn-ID Sundanese (Latin, Indonesia)
sv Swedish
sv-AX Swedish (Åland Islands)
sv-FI Swedish (Finland)
sv-SE Swedish (Sweden)
sw Kiswahili
sw-CD Kiswahili (Congo [DRC])
sw-KE Kiswahili (Kenya)
sw-TZ Kiswahili (Tanzania)
sw-UG Kiswahili (Uganda)
syr Syriac
syr-SY Syriac (Syria)
ta Tamil
ta-IN Tamil (India)
ta-LK Tamil (Sri Lanka)
ta-MY Tamil (Malaysia)
ta-SG Tamil (Singapore)
te Telugu
te-IN Telugu (India)
teo Teso
teo-KE Teso (Kenya)
teo-UG Teso (Uganda)
tg Tajik
tg-TJ Tajik (Tajikistan)
th Thai
th-TH Thai (Thailand)
ti Tigrinya
ti-ER Tigrinya (Eritrea)
ti-ET Tigrinya (Ethiopia)
tig Tigre
tig-ER Tigre (Eritrea)
tk Turkmen
tk-TM Turkmen (Turkmenistan)
tn Setswana
tn-BW Setswana (Botswana)
tn-ZA Setswana (South Africa)
to Tongan
to-TO Tongan (Tonga)
tr Turkish
tr-CY Turkish (Cyprus)
tr-TR Turkish (Turkey)
ts Xitsonga
ts-ZA Xitsonga (South Africa)
tt Tatar
tt-RU Tatar (Russia)
twq Tasawaq
twq-NE Tasawaq (Niger)
tzm Central Atlas Tamazight
tzm-Arab Central Atlas Tamazight (Arabic)
tzm-Arab-MA Central Atlas Tamazight (Arabic, Morocco)
tzm-DZ Central Atlas Tamazight (Algeria)
tzm-MA Central Atlas Tamazight (Morocco)
tzm-Tfng Central Atlas Tamazight (Tifinagh)
tzm-Tfng-MA Central Atlas Tamazight (Tifinagh, Morocco)
ug Uyghur
ug-CN Uyghur (China)
uk Ukrainian
uk-UA Ukrainian (Ukraine)
ur Urdu
ur-IN Urdu (India)
ur-PK Urdu (Pakistan)
uz Uzbek
uz-Arab Uzbek (Arabic)
uz-Arab-AF Uzbek (Arabic, Afghanistan)
uz-Cyrl Uzbek (Cyrillic)
uz-Cyrl-UZ Uzbek (Cyrillic, Uzbekistan)
uz-Latn Uzbek (Latin)
uz-Latn-UZ Uzbek (Latin, Uzbekistan)
vai Vai
vai-Latn Vai (Latin)
vai-Latn-LR Vai (Latin, Liberia)
vai-Vaii Vai (Vai)
vai-Vaii-LR Vai (Vai, Liberia)
ve Venda
ve-ZA Venda (South Africa)
vi Vietnamese
vi-VN Vietnamese (Vietnam)
vo Volapük
vo-001 Volapük (World)
vun Vunjo
vun-TZ Vunjo (Tanzania)
wae Walser
wae-CH Walser (Switzerland)
wal Wolaytta
wal-ET Wolaytta (Ethiopia)
wo Wolof
wo-SN Wolof (Senegal)
xh isiXhosa
xh-ZA isiXhosa (South Africa)
xog Soga
xog-UG Soga (Uganda)
yav Yangben
yav-CM Yangben (Cameroon)
yi Yiddish
yi-001 Yiddish (World)
yo Yoruba
yo-BJ Yoruba (Benin)
yo-NG Yoruba (Nigeria)
zgh Standard Moroccan Tamazight
zgh-MA Standard Moroccan Tamazight (Morocco)
zh Chinese
zh-Hans Chinese (Simplified)
zh-Hans-CN Chinese (Simplified, China)
zh-Hans-HK Chinese (Simplified, Hong Kong SAR)
zh-Hans-MO Chinese (Simplified, Macao SAR)
zh-Hans-SG Chinese (Simplified, Singapore)
zh-Hant Chinese (Traditional)
zh-Hant-HK Chinese (Traditional, Hong Kong SAR)
zh-Hant-MO Chinese (Traditional, Macao SAR)
zh-Hant-TW Chinese (Traditional, Taiwan)
zu isiZulu
zu-ZA isiZulu (South Africa)

Typekonvertering af tal

Du har tit brug for at konvertere én datatype til en anden, og det kan ske implicit eller eksplicit.

Implicit datakonvertering betyder konvertering af tal fra en lille datatype (eksempelvis en 8-bit byte) til en stor datatype (eksempelvis en 32-bit int):

Implicit datakonvertering

Her behøver du ikke foretage dig andet end at tildele den store variabel værdien af den lille:

byte a = 10;
int b = a;
Det er straks værre den anden vej – fra en stor datatype til en lille datatype, fordi et tal repræsenteret på eksempelvis 32 bit kan risikere ikke at kunne være på 8-bit. Det hedder eksplicit datakonvertering:

Eksplicit datakonvertering

Her eksempelvis fra int til byte:

int a = 10;
// byte b = a; FEJL
Ved eksplicit datakonvertering bliver du nødt til at hjælpe kompileren, og det kan du gøre på flere måder. Det anbefalede er at benytte metoder fra klassen System.Convert således, at afrunding og fejlhåndtering sker på den rigtige måde. På klassen findes der eksempelvis metoder som ToByte, ToInt32, ToDouble og så videre, og disse metoder kan typisk kaldes på mange forskellige måder med mange forskellige datatyper.

Her er et par eksempler fra int til byte med brug af ToByte:

int a = 10;
byte b = System.Convert.ToByte(a); 
// det går fint - 10 er nu placeret i en byte
int c = 300;
byte d = System.Convert.ToByte(c); 
// vil fejle - 300 ikke kan være i en byte
Der er rigtig mange metoder på klassen, du kan bruge til at komme fra en datatype til en anden:
byte a = 0;
short b = 0;
int c = 0;
long d = 0;
a = System.Convert.ToByte(b);
a = System.Convert.ToByte(c);
a = System.Convert.ToByte(d);

double e = 3434.45;
float f = System.Convert.ToSingle(e);

og hvis du forsøger at konvertere tal, der kræver afrunding, vil Convert-metoderne også klare det:

double a = 100.96;
int b = System.Convert.ToInt32(a);  // b = 101

Sluttelig vil du nogle gange skulle hjælpe kompileren med at konvertere konstanter. Her kan du benytte et bogstav i slutningen af konstanten for at fortælle kompileren, at den skal opfatte tallet som en konkret type:

Kode Type
L eller l Konstanten er en long
F eller f Konstanten er en float
D eller d Konstanten er en double
M eller m Konstanten er en decimal

Det kan eksempelvis bruges som følger:

long a = 1001L;     // L = long
float b = 1002.34F; // F = float
Hvis du ikke angiver et bogstav, vil kompileren opfatte heltal som int og reelle tal som double.

Brugen af disse konstantkoder er især brugbart ved metodekald, hvor du skal sende en konkret type med, samt ved erklæring af variabler med var-kodeordet, som vi kigger på senere:

float res = LægSammen(10.4F, 20.7F);

float LægSammen(float a, float b) {
    return a + b;
}
I koden kaldes en metode, der har argumenter af typen float. Hvis metoden skal kaldes med konstanter og ikke variabler, bliver du nødt til at fortælle kompileren, at 10,4 og 20,7 ikke er af typen double (default), men derimod float.

Der findes ligeledes konstantkoder til at konvertere binære (0b) og hexadecimale (0x) konstanter. Se mere i dokumentationen på MSDN

Opgaver

Sand eller falsk

Hvis du skal bruge en variabel, der kan få værdien sand eller falsk, kan du benytte 8-bit typen System.Boolean. De fleste benytter dog genvejsnavnet bool, og en variabel af denne type kaldes typisk for en boolsk variabel:

Typenavn Genvej Størrelse
System.Boolean bool 8-bit

Den er simpel i brug, fordi den kun kan tildeles værdien sand eller falsk og ikke som i nogen andre sprog andre værdier, som så typekonverteres automatisk.

Som i de fleste simple variabeltyper kan du vælge, om du vil benytte typenavnet (System.Boolean) eller genvejsnavnet (bool):

System.Boolean a;
a = true;

bool b;
b = false;

bool c = true;
bool d, e, f;

Husk, at når du ser koden

bool a = true;
står der egentlig

System.Boolean a = new System.Boolean();
a = true;
Det er lidt mere logisk at læse (giv mig en variabel a, der kan indeholde en System.Boolean – og skab en ny instans og tildel den til variablen).

Info

Ved du hvorfor en sand/falsk variabeltype hedder en bool eller boolean i de fleste sprog? Fra 1815 til 1864 levede en engelsk matematiker og filosof ved navn George Boole. Han var professor ved universitetet Queen’s College i Cork i Irland og er ophavsmand til en speciel form for algebra, der benytter operatorer og variabler med de to logiske værdier – sand eller falsk. Hans tanker og teorier ligger til grund for udvikling af logiske porte, som igen er grundlaget for CPU-konstruktion. Se mere her.

Boolske operatorer

En boolsk variabel benyttes især ved styring og kontrol af programflow. Kode som hvis dette er sandt så gør dette ellers så gør dette, er grundlaget for al programmering. Der findes derfor et par logiske operatorer, som du skal kende til:

Operator Forklaring
== Samme som
!= Forskellig fra
&& And (og) – begge værdier skal være sande for at returnere sand
|| Or (eller) – kun en værdi skal være sand for at returnere sand
! Not (modsat værdi)
Større end
Mindre end
>= Større end eller lig med
<= Mindre end eller lig med

De benyttes typisk i if-instruktioner, som du skal se senere, men kan også benyttes til at tildele værdier til variabler:

bool a = true;      // a = true
bool b = false;     // b = false

bool c = a == b;    // c = false
bool d = a != b;    // d = true
bool e = a && b;    // e = false
bool f = a || b;    // f = true
bool g = !b;        // g = true
Du kan naturligvis kombinere operatorerne, som du vil:
bool a = true;              // a = true
bool b = false;             // b = false
bool c = !(a == b) && true; // c = true

Den sidste linje ser teknisk ud, men skal læses som den modsatte værdi af true=false OG true kan forkortes til den modsatte værdi af false OG true, som kan forkortes til true OG true, som er true.

Tip

Husk at operatoren AND (&&) returnerer sand, hvis begge (alle) værdier er sande, og OR (||) returnerer sand, hvis en af værdierne er sande. NOT (!) operatoren returnerer den modsatte værdi.

Dato

Opbevaring, og især beregning, af dato og tid kan være lidt af en udfordring, og derfor stiller .NET flere strukturer til rådighed, der kan hjælpe.

System.DateTime er en 64 bit struktur, som repræsenterer dato og tid fra 01-01-0001 til 31-12-9999, og består i virkeligheden af et meget stort tal, der dækker antallet af såkaldte ticks (100 nanosekunder). Du arbejder dog sjældent med ticks, men benytter instanser af strukturen som en repræsentation af dato og tid:

Typenavn Genvej Størrelse
System.DateTime - 64-bit

System.DateTime har mystisk nok ikke noget genvejsnavn (som int eller bool), og der er heller ikke nogen måde at skrive en dato og tid som en konstant. Du er for det meste tvunget til at initialisere værdien ved hjælp af strukturens konstruktør (den kode der afvikles, når der skabes en ny instans), og dem er der en del af. De meste brugte er som følger:

// Ikke initialiseret
System.DateTime a;

// Samme som ovenfor - men kræver "using System;"
DateTime b;

// Initialiseret til 1-1-1 0:0:0
DateTime c = new DateTime();

// d initialiseret til 15/10-2019
DateTime d = new DateTime(2019, 10, 15);

// e initialiseret til 15/10-2019 kl. 8:15
DateTime e = new DateTime(2019, 10, 15, 8, 15, 0);
Der findes en del andre måder at initialisere en DateTime-variabel på, men mange er relateret til brug af forskellige tidszoner og kalendere og ligger uden for denne bogs rammer.

DateTime-typen har ligeledes nogle statiske medlemmer, som kan benyttes ved initialisering:

// Lokal systemtid (dato og tid)
DateTime a = DateTime.Now;

// Lokal systemdato 
DateTime b = DateTime.Today;
Når først du har fat i en DateTime-variabel, er der en masse praktiske egenskaber og metoder på selve instansen til rådighed:
// Lokal systemtid (dato og tid)
DateTime a = DateTime.Now;
int dagIMåned = a.Day;
DayOfWeek ugedag = a.DayOfWeek; 
int time = a.Hour;
int minut = a.Minute;
int month = a.Month;
int sekund = a.Second;
int år = a.Year;

Nogle metoder returnerer en ny DateTime-variabel:

DateTime a = new DateTime(2019, 10, 17);
Console.WriteLine(a.ToShortDateString());   // 17-10-2019
a.AddDays(1);
Console.WriteLine(a.ToShortDateString());   // 17-10-2019

a = a.AddDays(1);
Console.WriteLine(a.ToShortDateString());   // 18-10-2019
Du kan måske undre dig over, at linjen:

a.AddDays(1);
ikke tilretter variablen a, men det skyldes, at AddDays-metoden ikke tilretter den eksisterende værdi, men returnerer en helt ny, og at denne nye værdi ignoreres.

På linjen:

a = a.AddDays(1);

gemmes værdien i en variabel. Det kunne være en helt ny variabel, men hvis du ikke har noget at bruge den gamle værdi til, kan du lige så godt genbruge variablen.

Hvis du er i tvivl om, hvad en metode returnerer, bliver du nødt til at se i dokumentationen, eller være opmærksom på hvad Visual Studio fortæller dig:

AddDays-metoden returnerer en ny DateTime

Bemærk, at Visual Studio (Code) fortæller dig, at der på instanser af DateTime (1) findes metoden AddDays (2), der som argumenter tager en double (3), og at den returnerer en DateTime (4). Læs også beskrivelsen af metoden.

I programmeringsteori kalder man dette for immutable data fordi den underliggende værdi ikke kan rettes, men skal tildeles en ny. DateTime-typen er, ligesom mange andre simple variabeltyper, en immutabel datatype, og årsagerne til, at nogle datatyper er immutable og andre er mutable, er relateret til sikkerhed, performance og optimering.

Tip

At arbejde med datoer fra den gregorianske kalender i samme tidszone er nogenlunde simpelt, men at arbejde med datoer hen over tidszoner kan være noget fnidder. DateTime-strukturen har medlemmer relateret til UTC, men måske skulle du i stedet se på System.DateTimeOffset, som er bygget til at håndtere beregninger med tidszoner.

Info

Se i øvrigt NuGet-pakken DateTimeExtensions, som indeholder en masse metoder til at arbejde med dato og tid. Se mere her og her.

Tid

Hvis du vil repræsentere tid, kan du bruge System.TimeSpan-strukturen – enten for blot at repræsentere tid som en værdi eller som resultatet af en beregning:

// uinitialiseret
TimeSpan a;

// initialiseret til 0:00
TimeSpan b = new TimeSpan();

// initialiseret til 10 timer, 15 minutter og 25 sekunder
TimeSpan c = new TimeSpan(10, 15, 25);

// initialiseret til 1 dag, 10 timer, 15 minutter og 25 sekunder
TimeSpan d = new TimeSpan(1, 10, 15, 25);
Et TimeSpan-objekt kan også komme fra en beregning mellem to DateTime-variabler:

DateTime a = new DateTime(2019, 1, 1);
DateTime b = new DateTime(2019, 8, 28);
TimeSpan c = b.Subtract(a);
TimeSpan d = b - a;  // samme som Subtract
Da TimeSpan repræsenterer tid, kan den jo opfattes på forskellig måde (minutter, timer, dage, måneder med videre), og derfor indeholder TimeSpan instanser forskellige egenskaber relateret til, hvordan du ønsker at repræsentere tid:
DateTime a = new DateTime(2019, 1, 1, 15, 20, 0);
DateTime b = new DateTime(2019, 8, 28, 8, 0, 0);
TimeSpan c = b - a;

int antalMinutter = c.Minutes;                  
// 40 (antal minutter mellem 0 og 20 minutter)

double totalAntalMinutter = c.TotalMinutes;     
// 343.780 (total antal minutter mellem a og b)

int antalTimer = c.Hours;                       // 16
double totalAntalTimeDouble = c.TotalHours;     // 5.728,66

int antalDage = c.Days;                         // 238
double TotalAntalDage = c.TotalDays;            // 238,69

Og sluttelig en masse muligheder for at regne på tid.

TimeSpan a = new TimeSpan(8, 0, 0);

TimeSpan b = a * 5;                              
// 8 timer * 5

TimeSpan c = a / 2;                              
// 8 timer / 2

TimeSpan d = a.Add(new TimeSpan(0, 90, 0));      
// 8 timer + 90 minutter

TimeSpan e = a.Subtract(new TimeSpan(0, 90, 0)); 
// 8 timer - 90 minutter

Formatering af dato og tid

En instans af en DateTime indeholder metoderne ToShortDateString, ToLongDateString, ToShortTimeString og ToLongTimeString, som du kan bruge til en hurtig formatering, men ligesom tal kan dato og tid også udskrives eller gemmes i formateret form ved brug af formateringstegn.

Der er rigtig mange at vælge imellem, men her er de vigtigste:

Tegn Forklaring
dd Dag med eventuelt foranstillet 0
ddd Kort navn på dag (ma, ti …)
dddd Langt navn på dag (mandag, tirsdag, …)
MM Måned med eventuelt foranstillet 0
MMM Kort navn på måned
MMMM Langt navn på måned
y År (9)
yy År med eventuelt foranstillet nul (09)
yyyy Langt år (2009)
mm Minut med eventuelt foranstillet 0
HH Time med eventuelt foranstillet 0
ss Sekund med eventuelt foranstillet 0

Du kan eventuelt benytte ToString-metoden til formatering:

// Forudsætter afvikling på en dansk maskine med dansk (da-DK) kultur
DateTime a = new DateTime(2019, 10, 17, 13, 37, 25);

// brug af indbyggede metoder
Console.WriteLine(a.ToShortDateString());   // 17-10-2019
Console.WriteLine(a.ToLongDateString());    // 17. oktober 2019
Console.WriteLine(a.ToShortTimeString());   // 13:37
Console.WriteLine(a.ToLongTimeString());    // 13:37:25

// brug af ToString og formateringstegn
Console.WriteLine(a.ToString());            // 17-10-2019 13:37:25
Console.WriteLine(a.ToString("dd"));        // 17
Console.WriteLine(a.ToString("ddd"));       // to   
Console.WriteLine(a.ToString("dddd"));      // torsdag
Console.WriteLine(a.ToString("MM"));        // 10
Console.WriteLine(a.ToString("MMM"));       // okt
Console.WriteLine(a.ToString("yy"));        // 19
Console.WriteLine(a.ToString("yyyy"));      // 2019

Console.WriteLine(a.ToString("HH"));        // 13
Console.WriteLine(a.ToString("mm"));        // 37
Console.WriteLine(a.ToString("ss"));        // 25

Console.WriteLine(a.ToString("dd-MM-yyyy"));// 17-10-2019
Console.WriteLine(a.ToString("ddMMyyyy"));  // 17102019
Console.WriteLine(a.ToString("yyyyMMdd"));  // 20191017

Men ligesom ved formatering af tal bør du måske angive en kultur for at sikre, at formatering er ligegyldig, uanset hvilken maskine der afvikles på. Det kan gøres i de enkelte metoder eller ved at sætte kultur en gang for alle:

System.Globalization.CultureInfo c = new 
        System.Globalization.CultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentCulture = c;

DateTime a = new DateTime(2019, 10, 17, 13, 37, 25);
Console.WriteLine(a.ToShortDateString());   // 10/17/2019
Console.WriteLine(a.ToLongDateString());    
// Thursday, October 17, 2019
Console.WriteLine(a.ToShortTimeString());   // 1:37 PM
Console.WriteLine(a.ToLongTimeString());    // 1:37:25 PM

Console.WriteLine(a.ToString());            // 10/17/2019 1:37:25 PM
Console.WriteLine(a.ToString("dd"));        // 17
Console.WriteLine(a.ToString("ddd"));       // Thu   
Console.WriteLine(a.ToString("dddd"));      // Thursday
Console.WriteLine(a.ToString("MM"));        // 10
Console.WriteLine(a.ToString("MMM"));       // okt
Console.WriteLine(a.ToString("yy"));        // 19
Console.WriteLine(a.ToString("yyyy"));      // 2019

Console.WriteLine(a.ToString("HH"));        // 13
Console.WriteLine(a.ToString("mm"));        // 37
Console.WriteLine(a.ToString("ss"));        // 25

Console.WriteLine(a.ToString("dd-MM-yyyy"));// 17-10-2019
Console.WriteLine(a.ToString("ddMMyyyy"));  // 17102019
Console.WriteLine(a.ToString("yyyyMMdd"));  // 20191017

Typekonvertering af dato og tid

Såvel DateTime som TimeSpan kan typekonverteres fra strenge af mange forskellige formater, og der er mange måder at gøre det på. Du kan vælge at benytte metoder fra Convert-klassen

DateTime a = Convert.ToDateTime("2019-10-15");
DateTime b = Convert.ToDateTime("2019-10-15T20:15:30");
DateTime c = Convert.ToDateTime("15. Oktober 2019");

eller eksempelvis den statiske Parse-metode fra DateTime/TimeSpan-strukturen selv:

DateTime a = DateTime.Parse("2019-10-15");
DateTime b = DateTime.Parse("2019-10-15T20:15:30");
DateTime c = DateTime.Parse("15. Oktober 2019");
TimeSpan d = TimeSpan.Parse("15:25");

Du skal dog være opmærksom på, hvilket format der konverteres fra. Førnævnte kode forventer en dansk kultur, men det anbefales enten at angive kulturen:

DateTime a = DateTime.Parse("15. Oktober 2019 20:15", new 
     System.Globalization.CultureInfo("da-DK"));
eller eventuelt at benytte ParseExact-metoden
DateTime a = DateTime.ParseExact("15102019", "ddMMyyyy", new 
     System.Globalization.CultureInfo("da-DK"));

Formatet på strengen (ddMMyyyy) angives med specielle tegn, som er generelle for både konvertering og formatering.

Opgaver

DateTimeOffset

Der findes ligeledes en DateTimeOffset-struktur, som minder meget om DateTime-strukturen. Den kan benyttes hvis man ønsker større kontrol over tidzoner. Se mere på https://docs.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime.

DateOnly og TimeOnly

I C# 10 blev to nye typer tilføjet System.DateOnly og System.TimeOnly. De har som navnet antyder til formål at repræsentere en dato og et tidspunkt (og ikke begge dele som System.DateTime). Det kan gøre det lidt nemmere at arbejde med enten datoer eller tidspunkter.

Her er et par eksempler på brugen af DateOnly:

DateOnly d1 = new DateOnly(2021, 5, 31);
Console.WriteLine(d1.Year);      // 2021
Console.WriteLine(d1.Month);     // 5
Console.WriteLine(d1.Day);       // 31
Console.WriteLine(d1.DayOfWeek); // Mandag

// Manipulation
DateOnly d2 = d1.AddMonths(1);
Console.WriteLine(d2);

int days = d2.DayNumber - d1.DayNumber;
Console.WriteLine($"{days} dage mellem {d1} og {d2}");

DateOnly d3 = DateOnly.ParseExact("24 Dec 2021", "dd MMM yyyy", System.Globalization.CultureInfo.InvariantCulture);
Console.WriteLine(d3);

DateOnly nu = DateOnly.FromDateTime(DateTime.Today);
Console.WriteLine(nu);

DateTime dt = d3.ToDateTime(new TimeOnly(0, 0));
Console.WriteLine(dt);
Og her er et par eksempler på brugen af TimeOnly:
TimeOnly t1 = new TimeOnly(16, 30);
Console.WriteLine(t1.Hour);      // 16
Console.WriteLine(t1.Minute);    // 30
Console.WriteLine(t1.Second);    // 0

TimeOnly t2 = t1.AddHours(10);  // 02:30

// Hvor mange dage er der i 'overskud'
TimeOnly t3 = t2.AddMinutes(5000, out int wrappedDays); // 13:50, 3 (3 dage efter)


// Pas på rækkefølge når der trækkes fra
TimeOnly t4 = new TimeOnly(2, 0);
TimeOnly t5 = new TimeOnly(21, 0);
TimeSpan x = t5 - t4;   // 19
TimeSpan y = t4 - t5;   // 5

TimeOnly t6 = TimeOnly.ParseExact("5:00 pm", "h:mm tt", System.Globalization.CultureInfo.InvariantCulture);  // 17:00

// Til TimeSpan
TimeSpan ts = t6.ToTimeSpan();      // "17:00:00"

// Til DateTime
DateTime dt = new DateOnly(1970, 1, 1).ToDateTime(t6);

TimeOnly nu = TimeOnly.FromDateTime(DateTime.Now);

// I mellem ...
if (nu.IsBetween(t1, t2))
{}
else
{}

Du kan vælge at holde fast i brugen af System.DateTime. Den har levet siden den allerførste version af C#, og der findes derfor en masse eksempler og dokumentation. Men de to nye typer kan gøre koden mere simpel.

Guid

I C# er en Guid (Globally Unique Identifier) en struktureret datatype, der anvendes til at skabe unikke værdier. Disse værdier bruges ofte som entydige nøgler i databaser, som identifikatorer i distribuerede systemer eller som unikke referencer for objekter. En Guid er en 128-bit værdi, hvilket gør den stor nok til at sikre unikke værdier, selv når de genereres i store mængder og på tværs af forskellige systemer.

Her er et par eksempler på Guids:

d8dc8f6f-0706-4164-8443-61af48a5d496
5d19a739-d9ab-41ab-b830-37124f7f57ca
6889e1d1-2f86-4bc7-9a91-ef1f39a9bd1d
ee5f3a39-304f-4433-aa72-13863800e1a6

Du kan oprette en Guid på flere måder i C#. En almindelig metode er at bruge Guid.NewGuid()-metoden, som automatisk genererer en ny, unik Guid:

Guid newGuid = Guid.NewGuid();
Console.WriteLine(newGuid.ToString());

Dette vil udskrive en ny Guid som en streng, for eksempel: d9b1d7db-5b19-4ca3-8e4a-ef95a8e71834.

Du kan også oprette en Guid ud fra en streng ved at bruge Guid.Parse() eller Guid.TryParse():

string guidString = "d9b1d7db-5b19-4ca3-8e4a-ef95a8e71834";
Guid parsedGuid = Guid.Parse(guidString);
Console.WriteLine(parsedGuid);

Hvis strengen ikke er i et korrekt format, vil Guid.Parse() kaste en undtagelse, mens Guid.TryParse() vil returnere false.

Guid-værdier bruges bredt i softwareudvikling til formål, hvor unikke identifikatorer er nødvendige. Dette inkluderer:

  • Databaser: Som primærnøgler i tabeller, hvilket sikrer, at hver række kan entydigt identificeres.
  • Distribuerede systemer: Til at spore og identificere ressourcer på tværs af flere systemer og tjenester.
  • Objektreferencer: I applikationer, hvor objekter skal kunne identificeres entydigt, f.eks. i cache-systemer.

Brugen af Guid i C# gør det nemt at arbejde med unikke identifikatorer uden at bekymre sig om kollisioner, selv i store og komplekse systemer.

UUID

UUID (Universally Unique Identifier) er et standardiseret format for Guid og bruges i mange programmeringssprog og systemer. I praksis er Guid og UUID det samme, da de begge er 128-bit værdier og følger samme grundlæggende struktur. I C# anvendes termen Guid, men den repræsenterer samme koncept som UUID. Der er flere versioner af UUID’er, som definerer, hvordan de genereres og bruges. I kommende version af C# (version 13 - .NET 9) forventes UUID version 7 at blive understøttet. Det er en tidsbaseret UUID, der forbedre unikhed og sortering.

Brug af var i C#

Du kan bruge nøgleordet var til at lade kompilatoren automatisk bestemme typen af en variabel baseret på den værdi, du tildeler den. Det kan gøre din kode lidt mere kompakt og lettere at læse, især når typen er tydelig fra tildelingen:

var number = 5;   // Kompilatoren ved, at number er af typen int
var text = "Hej"; // Kompilatoren ved, at text er af typen string

I dette eksempel: - number bliver automatisk til en int (heltal), fordi den er tildelt værdien 5. - text bliver en string (tekst), fordi den er tildelt en tekststreng.

var er praktisk, når typen er indlysende fra konteksten, eller når du arbejder med lange eller komplekse typer, såsom resultater fra metoder, der returnerer samlinger.

var customers = new List<string>(); // List<string> er en "lang" type

Der er dog nogle begrænsninger:

  • var kan ikke bruges uden en tildeling, da kompilatoren ikke vil kunne bestemme typen:
var x; // Dette giver en fejl, fordi der ikke er nogen værdi at udlede typen fra
  • var gør koden mindre eksplicit, så hvis typen ikke er åbenlys, bør du overveje at skrive typen direkte.
  • Det kan være mindre tydeligt, hvilken type en variabel har, hvis det ikke er klart ud fra tildelingen.

men også fordele:

  • Kortere kode.
  • Mere læsbar kode, når typen er indlysende.

Generelt er det en god idé at bruge var, når det gør koden nemmere at læse, men undgå at bruge det, hvis det gør koden sværere at forstå.

Hvad er en datatype egentlig

Helt grundlæggende er en datatype som int, bool, DateTime mv. blot en type Microsoft har skabt for at repræsentere en speciel værdi, og du kan se en type som en skabelon for hvordan værdien skal gemmes i hukommelsen og hvordan de ønsker man skal kunne arbejde med den. Man kan ikke helt selv skabe datatyper på samme måde fordi Microsofts typer er bagt ind i frameworket på et niveau under hvad vi selv kan - men vi kan komme tæt på.

Her er eksempelvis brug af en datatype som repræsenterer det romerske talsystem (dog kun et tegn som I, V, X, L, C, D, og M), og som kan bruges som:

RomerskVærdi a = 'X'; 
Console.WriteLine(a);               // X
Console.WriteLine(a.TilTal());      // 10

RomerskVærdi b = new RomerskVærdi();
b = 'V';
Console.WriteLine(b);               // V
Console.WriteLine(b.TilTal());      // 5

Console.WriteLine(a > b);           // true
Console.WriteLine(a < b);           // false
Console.WriteLine(a == b);          // false
Console.WriteLine(a != b);          // true

Læg mærke til hvor meget det minder om en int eller bool eller andre typer. Du behøver ikke forstå koden bag RomerskVærdi men det er vigtigt at forstå, at en datatype ‘blot’ er en skabelon skabt af MS for hvordan man repræsenterer forskellige værdier.

Skabelonen bag RomerskVærdi
public struct RomerskVærdi
{
    private char numeral;

    public RomerskVærdi()
    {
        numeral = 'I';
    }

    public RomerskVærdi(char numeral)
    {
        if ("IVXLCDM".IndexOf(numeral) == -1)
            throw new ArgumentException("Forkert tegn");

        this.numeral = numeral;
    }

    public static implicit operator RomerskVærdi(char numeral)
    {
        return new RomerskVærdi(numeral);
    }

    public int TilTal()
    {
        switch (numeral)
        {
            case 'I': return 1;
            case 'V': return 5;
            case 'X': return 10;
            case 'L': return 50;
            case 'C': return 100;
            case 'D': return 500;
            case 'M': return 1000;
            default: throw new InvalidOperationException("Forkert tegn");
        }
    }

    public static bool operator ==(RomerskVærdi a, RomerskVærdi b)
    {
        return a.numeral == b.numeral;
    }

    public static bool operator !=(RomerskVærdi a, RomerskVærdi b)
    {
        return !(a == b);
    }

    public static bool operator >(RomerskVærdi a, RomerskVærdi b)
    {
        return a.TilTal() > b.TilTal();
    }

    public static bool operator <(RomerskVærdi a, RomerskVærdi b)
    {
        return a.TilTal() < b.TilTal();
    }


    public override bool Equals(object? obj)
    {
        if (obj is RomerskVærdi other)
        {
            return numeral == other.numeral;
        }

        return false;
    }

    public override int GetHashCode()
    {
        return numeral.GetHashCode();
    }

    public override string ToString()
    {
        return numeral.ToString();
    }
}

Introduktion til stack og heap

Når man starter med at programmere i C#, er det vigtigt at forstå forskellen mellem stack og heap. Det er de to områder i hukommelsen, hvor C# gemmer data, men de håndteres forskelligt.

Stack’en er et område i hukommelsen, hvor data opbevares i en meget struktureret rækkefølge. Når en metode kaldes, reserveres et nyt område på stakken til denne funktion. Dette område kaldes en stackframe og indeholder metodens lokale variabler. Når metoden er færdig, fjernes stackframe’n, og hukommelsen bliver frigjort.

Heap’en er et mere fleksibelt område i hukommelsen. Når du opretter objekter (med nøgleordet new i C#), bliver de placeret på heap’en. Hukommelsen i heap’en forvaltes ikke automatisk på samme måde som stakken. I C# tager garbage collector’en sig af at rydde op og frigøre hukommelse, som ikke længere er i brug. Objekter på heap’en kan nås fra forskellige dele af programmet og har ikke samme livscyklus som variabler på stakken.

Klassisk stack og Heap diagram

Det er vigtigt at huske, at i C# er værdityper (som int, double, bool, osv.) typisk opbevaret på stakken, mens reference-typer (som objekter af klasser skabt med new) opbevares på heap’en. Dette har betydning for, hvordan data overføres i programmet og hvordan hukommelse håndteres.

Husk…

  • Alle variabler placeres på stakken, når de er deklareret.
    • Hvis der er tale om en værditype, indeholder variablen selve værdien.
    • Hvis der er tale om en reference-type, indeholder variablen en reference til objektet på heap’en.

De variabeltyper vi har set på indtil nu, er alle værdityper (alle er baseret på structs).

Det er super vigtigt at kunne “tegne” et stack og heap diagram i hovedet - det gør alle professionelle udvikler helt automatisk. Indtil du lige har fået det på rygraden, kan du bruge et værktøj som SharpLap til at lave diagrammerne. Prøv at skrive følgende kode ind i SharpLab, vælge Run i højre vindue, og se hvad der sker:

using System;

int a = 10;
double b = 20.5;
bool c = true;
Datetime d = new DateTime(2021, 1, 1);
string e = "Hello";
Inspect.MemoryGraph(a, b, c, d, e);

Visualisering af stack og heap gennem SharpLab.io

Bemærk at de fleste variabler ender på stack’en (værdityper), mens string’en ender på heap’en (reference-type). Mere om string senere - men her kan du se forskellen direkte.