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:
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:
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:
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:
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.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
}
}
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
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:
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:
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:
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:
som
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:
De fleste vælger (naturligvis) at bruge genvejsnavnet og initialisere med det samme:Så længe du arbejder med samme type, må du gerne erklære flere variabler i samme instruktion:
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:
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: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
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
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");
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):
Her behøver du ikke foretage dig andet end at tildele den store variabel værdien af den lille:
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:Her eksempelvis fra int til byte:
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
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:
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:
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:
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):
Husk, at når du ser koden
står der egentlig 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
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);
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;
// 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
På linjen:
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:
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);
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
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"));
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);
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
:
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.
Der er dog nogle begrænsninger:
var
kan ikke bruges uden en tildeling, da kompilatoren ikke vil kunne bestemme typen:
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.
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: