Examinați sistemul de tip .NET
C# este o limbă tastată puternic. Fiecare variabilă și constantă are un tip, la fel ca fiecare expresie care se evaluează la o valoare. Biblioteca de clasă .NET definește tipuri numerice predefinite și tipuri complexe care reprezintă o largă varietate de construcții. Aceste construcții includ sistemul de fișiere, conexiunile de rețea, colecțiile și matricele de obiecte și date. Un program C# tipic utilizează tipuri din biblioteca de clasă și tipurile definite de utilizator care modelează conceptele specifice domeniului problemei programului.
Tipuri predefinite
C# oferă un set standard de tipuri predefinite. Aceste tipuri standard reprezintă numere întregi, valori în virgulă mobilă, expresii booleene, caractere text, valori zecimale și alte tipuri de date. Există, de asemenea, tipuri de șiruri și obiecte predefinite. Aceste tipuri sunt disponibile pentru utilizare în orice program C#.
Tipuri particularizate
Utilizați struct, class, interface, enumși construcții record pentru a vă crea propriile tipuri particularizate. Biblioteca de clasă .NET este o colecție de tipuri particularizate pe care le puteți utiliza în aplicațiile dvs. În mod implicit, tipurile utilizate cel mai frecvent din biblioteca de clasă sunt disponibile în orice program C#. Alte persoane devin disponibile doar atunci când adăugați în mod explicit o referință de proiect la asamblarea care le definește. După ce compilatorul are o referință la asamblare, puteți declara variabile (și constante) ale tipurilor declarate în acea asamblare în codul sursă.
Sistem de tip comun
Este important să înțelegeți două puncte fundamentale despre sistemul de tip din .NET:
Acesta susține principiul moștenirii. Tipurile pot deriva din alte tipuri, denumite tipuri de bază. Tipul derivat moștenește (cu unele restricții) metodele, proprietățile și alți membri ai tipului de bază. Tipul de bază poate deriva la rândul său din alt tip, caz în care tipul derivat moștenește membrii ambelor tipuri de bază în ierarhia sa de moștenire. Toate tipurile, inclusiv tipurile numerice predefinite, cum ar fi System.Int32 (cuvânt cheie C#: int), derivați în cele din urmă dintr-un singur tip de bază, care este
System.Object(cuvânt cheie C#:object). Această ierarhie de tip unificat se numește Sistem de tip comun (CTS).Fiecare tip din CTS este definit ca tip de valoare sau tip de referință. Aceste tipuri includ toate tipurile particularizate din biblioteca de clase .NET și, de asemenea, propriile tipuri definite de utilizator. Tipurile pe care le definiți utilizând cuvântul cheie struct sunt tipuri de valori; toate tipurile numerice predefinite sunt structură. Tipurile pe care le definiți utilizând cuvântul cheie de clasă sau de înregistrare sunt tipuri de referințe. Tipurile de referințe și tipurile de valori au reguli diferite privind timpul de compilare și un comportament diferit de rulare.
Următoarea ilustrație arată relația dintre tipurile de valori și tipurile de referință din CTS.
Clasele și structurile sunt două dintre construcțiile de bază ale sistemului de tip comun din .NET. Fiecare este, în esență, o structură de date care încapsulează un set de date și comportamente care aparțin împreună ca unitate logică. Datele și comportamentele sunt membrii class, structsau record. Membrii unei clase includ proprietăți (date), metode (comportamente), câmpuri (variabile declarate în clasă) și multe altele.
O declarație class, structsau record este ca o schiță utilizată pentru a crea instanțe sau obiecte la momentul rulării. Dacă definiți o clasă denumită Person, Person este numele tipului. Dacă declarați și inițializați o variabilă p de tip Person, se spune că p este un obiect sau o instanță de Person. Pot fi create mai multe instanțe de același tip de Person și fiecare instanță poate avea valori diferite în proprietățile și câmpurile sale.
O clasă este un tip de referință. Atunci când se creează un obiect de tip, variabila la care este atribuit obiectul conține doar o referință la acea memorie. Atunci când referința obiectului este atribuită unei variabile noi, variabila nouă se referă la obiectul original. Modificările efectuate printr-o variabilă se reflectă în cealaltă variabilă, deoarece ambele fac referire la aceleași date.
Un struct este un tip de valoare. Atunci când se creează o struct, variabila la care este atribuită struct conține datele reale ale struct. Atunci când struct este atribuită unei variabile noi, valoarea de date este copiată. Noua variabilă și variabila originală conțin, prin urmare, două copii separate ale acelorași date. Modificările efectuate într-o copie nu afectează cealaltă copie.
Tipurile de înregistrări pot fi tipuri de referințe (record class) sau tipuri de valori (record struct). Tipurile de înregistrări conțin metode care acceptă egalitatea de valori.
În general, clasele sunt utilizate pentru a modela un comportament mai complex. Clasele stochează de obicei date care sunt menite să fie modificate după crearea unui obiect de clasă. Structurile sunt cele mai potrivite pentru structurile de date mici. Structurile stochează de obicei date care nu sunt destinate modificării după crearea struct. Tipurile de înregistrări sunt structuri de date cu alți membri sintetizați ai compilatorului. Înregistrările stochează de obicei date care nu sunt destinate modificării după crearea obiectului.
Tipuri de valori
Tipurile de valori derivă din System.ValueType, care derivă din System.Object. Tipurile care derivă din System.ValueType au un comportament special în modulul runtime comun al limbii. Variabilele de tip de valoare conțin direct valorile lor. Memoria pentru o struct este alocată în linie în orice context este declarată variabila. Nu există nicio alocare de heap separată sau colectare de deșeuri pentru variabilele de tip valoare. Aveți posibilitatea să declarați tipuri de structură de înregistrări care sunt tipuri de valori și să includeți membrii sintetizați pentru înregistrări.
Există două categorii de tipuri de valori: structură și enumerare.
Tipurile numerice predefinite sunt structură și au câmpuri și metode pe care le puteți accesa:
// constant field on type byte.
byte b = byte.MaxValue;
Dar declarați și atribuiți valori acestora ca și cum ar fi simple tipuri neagregate:
byte num = 0xA;
int i = 5;
char c = 'Z';
Tipurile de valori sunt sigilate. Nu puteți deriva un tip din niciun tip de valoare, de exemplu, System.Int32. Nu puteți defini o struct de moștenit de la orice class sau struct definite de utilizator, deoarece un struct poate moșteni doar de la System.ValueType.
Utilizați cuvântul cheie struct pentru a crea propriile tipuri de valori particularizate. De obicei, o structură este utilizată ca container pentru un set mic de variabile asociate, așa cum se arată în exemplul următor:
public struct Coords
{
public int x, y;
public Coords(int p1, int p2)
{
x = p1;
y = p2;
}
}
Cealaltă categorie de tipuri de valori este enum. Un enum definește un set de constante integrale denumite. De exemplu, enumerarea System.IO.FileMode din biblioteca de clasă .NET conține un set de numere întregi constante denumite care specifică modul în care trebuie deschis un fișier. Sintaxa pentru o enum se afișează în următorul exemplu:
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
Constanta System.IO.FileMode.Create are valoarea 2. Totuși, numele este mult mai semnificativ pentru oameni citind codul sursă și, din acest motiv, este mai bine să utilizați enumerari în locul numerelor literale constante.
Toate enums moștenesc de la System.Enum, care moștenesc de la System.ValueType. Toate regulile care se aplică pentru structs se aplică și pentru enums.
Tipuri de referințe
Tipurile de class, record, delegate, arrayși interface sunt tipuri de referințe .
Atunci când declarați o variabilă de un tip de referință, aceasta conține valoarea null până când o atribuiți cu o instanță de acel tip sau creați una utilizând operatorul de new.
Următorul exemplu arată cum se declară variabilele de tip referință utilizând matrice:
// 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;
Operatorul new creează o instanță de tip și returnează o referință la acea instanță. Referința este adresa de memorie a obiectului și referința este stocată în variabilă. Atunci când atribuiți o variabilă de tip referință altei variabile, copiați referința, nu obiectul în sine. Ambele variabile se referă la același obiect din memorie.
Notă
În plus față de tipurile de referințe, matricele sunt colecții. Colecțiile pot fi inițializate utilizând expresii de colecție, care elimină cerința de a include cuvântul cheie new la declararea și inițializarea unei matrice într-o singură linie. De exemplu: int[] numbers = [ 1, 2, 3, 4, 5 ];.
Puteți crea o instanță a unei clase utilizând aceeași sintaxă utilizată pentru a instanția tipurile predefinite. Următorul exemplu arată cum să creați o instanță a unei clase:
MyClass myClass = new MyClass();
MyClass myClass2 = myClass;
Operatorul new creează o instanță a clasei și returnează o referință la acea instanță. Referința este adresa de memorie a obiectului și referința este stocată în variabilă. Atunci când atribuiți o variabilă de tip referință altei variabile, copiați referința, nu obiectul în sine. Ambele variabile se referă la același obiect din memorie.