Undersøg .NET-typesystemet

Fuldført

C# er et stærkt skrevet sprog. Hver variabel og konstant har en type, og det samme gør alle udtryk, der evalueres til en værdi. .NET-klassebiblioteket definerer indbyggede numeriske typer og komplekse typer, der repræsenterer en lang række konstruktioner. Disse konstruktioner omfatter filsystemet, netværksforbindelser, samlinger og matrixer af objekter og datoer. Et typisk C#-program bruger typer fra klassebiblioteket og brugerdefinerede typer, der udformer de begreber, der er specifikke for programmets problemdomæne.

Indbyggede typer

C# indeholder et standardsæt af indbyggede typer. Disse standardtyper repræsenterer heltal, flydende talværdier, booleske udtryk, teksttegn, decimalværdier og andre typer data. Der er også indbyggede streng- og objekttyper. Disse typer er tilgængelige, så du kan bruge dem i et hvilket som helst C#-program.

Brugerdefinerede typer

Du kan bruge konstruktionerne struct, class, interface, enumog record til at oprette dine egne brugerdefinerede typer. Selve .NET-klassebiblioteket er en samling af brugerdefinerede typer, som du kan bruge i dine egne programmer. De hyppigst anvendte typer i klassebiblioteket er som standard tilgængelige i et C#-program. Andre bliver kun tilgængelige, når du eksplicit føjer en projektreference til den assembly, der definerer dem. Når compileren har en reference til assemblyen, kan du deklarere variabler (og konstanter) for de typer, der er erklæret i den pågældende assembly i kildekoden.

Fælles typesystem

Det er vigtigt at forstå to grundlæggende punkter om typesystemet i .NET:

  • Det understøtter princippet om nedarvning. Typer kan stamme fra andre typer, kaldet basistyper. Den afledte type nedarver (med nogle begrænsninger) metoder, egenskaber og andre medlemmer af basistypen. Basistypen kan igen stamme fra en anden type, og i så fald arver den afledte type medlemmerne af begge basistyper i nedarvningshierarkiet. Alle typer, herunder indbyggede numeriske typer, f.eks. System.Int32 (nøgleordet C#: int), stammer i sidste ende fra en enkelt basistype, som er System.Object (nøgleordet C#: object). Dette samlede typehierarki kaldes CTS (Common Type System).

  • Hver type i CTS er defineret som enten en værditype eller en referencetype. Disse typer omfatter alle brugerdefinerede typer i .NET-klassebiblioteket og også dine egne brugerdefinerede typer. De typer, du definerer ved hjælp af nøgleordet struktur, er værdityper. alle de indbyggede numeriske typer er strukturer. De typer, du definerer ved hjælp af nøgleordet klasse eller post, er referencetyper. Referencetyper og værdityper har forskellige regler for kompileringstid og forskellig funktionsmåde for kørselstid.

Følgende illustration viser relationen mellem værdityper og referencetyper i CTS.

diagram, der viser værdi- og referencetyper.

Klasser og strukturer er to af de grundlæggende konstruktioner af det fælles typesystem i .NET. Hver enkelt er grundlæggende en datastruktur, der indkapsler et sæt data og funktionsmåder, der hører sammen som en logisk enhed. Dataene og funktionsmåderne er medlemmer af class, structeller record. Medlemmerne af en klasse omfatter egenskaber (data), metoder (funktionsmåder), felter (variabler, der er erklæret i klassen) og mange andre.

En erklæring af typen class, structeller record svarer til en kursusplan, der bruges til at oprette forekomster eller objekter på kørselstidspunktet. Hvis du definerer en klasse med navnet Person, er Person navnet på typen . Hvis du deklarerer og initialiserer en variabel p af typen Person, siges p at være et objekt eller en forekomst af Person. Der kan oprettes flere forekomster af samme Person type, og hver forekomst kan have forskellige værdier i dens egenskaber og felter.

En klasse er en referencetype. Når der oprettes et objekt af typen , indeholder den variabel, som objektet er tildelt, kun en reference til den pågældende hukommelse. Når objektreferencen tildeles til en ny variabel, henviser den nye variabel til det oprindelige objekt. Ændringer, der foretages via én variabel, afspejles i den anden variabel, fordi de begge refererer til de samme data.

En struct er en værditype. Når der oprettes en struct, indeholder den variabel, som struct er tildelt, de faktiske data for struct. Når struct tildeles til en ny variabel, kopieres dataværdien. Den nye variabel og den oprindelige variabel indeholder derfor to separate kopier af de samme data. Ændringer af én kopi påvirker ikke den anden kopi.

Posttyper kan enten være referencetyper (record class) eller værdityper (record struct). Posttyper indeholder metoder, der understøtter værdilighed.

Generelt bruges klasser til at udforme mere komplekse funktionsmåder. Klasser gemmer typisk data, der er beregnet til at blive ændret, når et klasseobjekt er oprettet. Strukturer er bedst egnet til små datastrukturer. Strukturer gemmer typisk data, der ikke er beregnet til at blive ændret, når struct er oprettet. Posttyper er datastrukturer med andre kompileringssyntetiserede medlemmer. Poster gemmer typisk data, der ikke er beregnet til at blive ændret, når objektet er oprettet.

Værdityper

Værdityper stammer fra System.ValueType, som stammer fra System.Object. Typer, der stammer fra System.ValueType har en særlig funktionsmåde i kørsel af almindeligt sprog. Værditypevariabler indeholder direkte deres værdier. Hukommelsen for en struct er allokeret indbygget i en hvilken som helst kontekst, variablen erklæres. Der er ingen separat allokering af heap eller spild af affaldsindsamling for værditypevariabler. Du kan deklarere poststrukturtyper, der er værdityper, og inkludere de syntetiserede medlemmer for poster.

Der er to kategorier af værdityper: struktur og optælling.

De indbyggede numeriske typer er strukturer, og de har felter og metoder, som du kan få adgang til:


// constant field on type byte.
byte b = byte.MaxValue;

Men du deklarerer og tildeler værdier til dem, som om de er simple ikke-aggregerede typer:


byte num = 0xA;
int i = 5;
char c = 'Z';

Værdityperne er forseglede. Du kan ikke udlede en type fra nogen værditype, f.eks. System.Int32. Du kan ikke definere en struct, der skal nedarves fra brugerdefinerede class eller struct, fordi en struct kun kan arve fra System.ValueType.

Du kan bruge nøgleordet struktur til at oprette dine egne brugerdefinerede værdityper. En struktur bruges typisk som en objektbeholder for et lille sæt relaterede variabler, som vist i følgende eksempel:


public struct Coords
{
    public int x, y;

    public Coords(int p1, int p2)
    {
        x = p1;
        y = p2;
    }
}

Den anden kategori af værdityper er enum. En enum definerer et sæt navngivne integralkonstanter. Den System.IO.FileMode optælling i .NET-klassebiblioteket indeholder f.eks. et sæt navngivne konstante heltal, der angiver, hvordan en fil skal åbnes. Syntaksen for en enum vises i følgende eksempel:


public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

Konstanten System.IO.FileMode.Create har en værdi på 2. Navnet er dog meget mere meningsfuldt for mennesker, der læser kildekoden, og derfor er det bedre at bruge optællinger i stedet for konstante konstante konstante tal.

Alle enums arver fra System.Enum, som arver fra System.ValueType. Alle de regler, der gælder for structs, gælder også for enums.

Referencetyper

Typerne class, record, delegate, arrayog interface er referencetyper.

Når du deklarerer en variabel af en referencetype, indeholder den værdien null, indtil du tildeler den med en forekomst af denne type eller opretter en ved hjælp af operatoren new.

I følgende eksempel vises det, hvordan du deklarerer referencetypevariabler ved hjælp af matrixer:


// Declaring an array variable
int[] numbers;

// Initializing the array with a size of 5
numbers = new int[5];

// Alternatively, declaring and initializing an array in one line
int[] numbers2 = new int[] { 1, 2, 3, 4, 5 };

// Assigning a reference to another variable
int[] numbers3 = numbers2;

Operatoren new opretter en forekomst af typen og returnerer en reference til den pågældende forekomst. Referencen er objektets hukommelsesadresse, og denne reference gemmes i variablen. Når du tildeler en referencetypevariabel til en anden variabel, kopierer du referencen og ikke selve objektet. Begge variabler refererer til det samme objekt i hukommelsen.

Seddel

Ud over at være referencetyper er matrixer samlinger. Samlinger kan initialiseres ved hjælp af samlingsudtryk, hvilket fjerner kravet om at inkludere nøgleordet new, når en matrix erklæres og initialiseres på én linje. For eksempel: int[] numbers = [ 1, 2, 3, 4, 5 ];.

Du kan oprette en forekomst af en klasse ved hjælp af den samme syntaks, der bruges til at instantiere de indbyggede typer. I følgende eksempel vises det, hvordan du opretter en forekomst af en klasse:


MyClass myClass = new MyClass();
MyClass myClass2 = myClass;

Operatoren new opretter en forekomst af klassen og returnerer en reference til den pågældende forekomst. Referencen er objektets hukommelsesadresse, og denne reference gemmes i variablen. Når du tildeler en referencetypevariabel til en anden variabel, kopierer du referencen og ikke selve objektet. Begge variabler refererer til det samme objekt i hukommelsen.