Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
C# is een sterk getypte taal. Elke variabele en constante heeft een type, net als elke expressie die resulteert in een waarde. Elke methodedeclaratie specificeert een naam, het type en het type (waarde, verwijzing of uitvoer) voor elke invoerparameter en voor de retourwaarde. De .NET-klassebibliotheek definieert ingebouwde numerieke typen en complexe typen die een grote verscheidenheid aan constructies vertegenwoordigen. Dit zijn onder andere het bestandssysteem, netwerkverbindingen, verzamelingen en matrices van objecten en datums. Een typisch C#-programma maakt gebruik van typen uit de klassebibliotheek en door de gebruiker gedefinieerde typen die de concepten modelleren die specifiek zijn voor het probleemdomein van het programma.
De informatie die in een type is opgeslagen, kan de volgende items bevatten:
- De opslagruimte die een variabele van het type vereist.
- De maximum- en minimumwaarden die deze kunnen vertegenwoordigen.
- De leden (methoden, velden, gebeurtenissen, enzovoort) die deze bevat.
- Het basistype waaruit het wordt overgenomen.
- De interfaces die worden geïmplementeerd.
- De bewerkingen die zijn toegestaan.
De compiler gebruikt typegegevens om ervoor te zorgen dat alle bewerkingen die in uw code worden uitgevoerd , veilig zijn. Als u bijvoorbeeld een variabele van het type int
declareert, kunt u met de compiler de variabele optellen en aftrekken. Als u dezelfde bewerkingen probeert uit te voeren op een variabele van het type bool
, genereert de compiler een fout, zoals wordt weergegeven in het volgende voorbeeld:
int a = 5;
int b = a + 2; //OK
bool test = true;
// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;
Opmerking
C- en C++-ontwikkelaars merken op dat in C# bool
niet converteerbaar is naar int
.
De compiler sluit de typegegevens in het uitvoerbare bestand in als metagegevens. De Common Language Runtime (CLR) gebruikt die metagegevens tijdens runtime om de veiligheid van het type verder te garanderen wanneer het geheugen wordt toegewezen en vrijgemaakt.
Typen opgeven in variabeledeclaraties
Wanneer u een variabele of constante in een programma declareert, moet u het type opgeven of het var
trefwoord gebruiken om het type door de compiler te laten afleiden. In het volgende voorbeeld ziet u enkele variabeledeclaraties die gebruikmaken van zowel ingebouwde numerieke typen als complexe door de gebruiker gedefinieerde typen:
// Declaration only:
float temperature;
string name;
MyClass myClass;
// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = [0, 1, 2, 3, 4, 5];
var query = from item in source
where item <= limit
select item;
De typen methodeparameters en retourwaarden worden opgegeven in de methodedeclaratie. De volgende handtekening toont een methode die een int
als invoerargument vereist en een tekenreeks retourneert:
public string GetName(int ID)
{
if (ID < names.Length)
return names[ID];
else
return String.Empty;
}
private string[] names = ["Spencer", "Sally", "Doug"];
Nadat u een variabele hebt gedeclareerd, kunt u deze niet opnieuw declareren met een nieuw type en kunt u geen waarde toewijzen die niet compatibel is met het gedeclareerde type. U kunt bijvoorbeeld een int
niet declareren en vervolgens een booleaanse waarde van true
toewijzen. Waarden kunnen echter worden geconverteerd naar andere typen, bijvoorbeeld wanneer ze worden toegewezen aan nieuwe variabelen of worden doorgegeven als methodeargumenten. Een typeconversie die geen gegevensverlies veroorzaakt, wordt automatisch uitgevoerd door de compiler. Voor een conversie die gegevensverlies kan veroorzaken, is een cast in de broncode vereist.
Zie Cast- en Typeconversies voor meer informatie.
Ingebouwde typen
C# biedt een standaardset ingebouwde typen. Deze vertegenwoordigen gehele getallen, drijvende-kommawaarden, Booleaanse expressies, teksttekens, decimale waarden en andere typen gegevens. Er zijn ook ingebouwde string
en object
typen. Deze typen zijn beschikbaar voor gebruik in elk C#-programma. Zie Ingebouwde typen voor de volledige lijst met ingebouwde typen.
Aangepaste typen
U gebruikt de struct
, class
, interface
, enum
en record
constructies om uw eigen aangepaste typen te maken. De .NET-klassebibliotheek zelf is een verzameling aangepaste typen die u in uw eigen toepassingen kunt gebruiken. Standaard zijn de meestgebruikte typen in de klassebibliotheek beschikbaar in elk C#-programma. Anderen worden alleen beschikbaar wanneer u expliciet een projectreferentie toevoegt aan de assembly die deze definieert. Nadat de compiler een verwijzing naar de assembly heeft, kunt u variabelen (en constanten) declareren van de typen die in die assembly in de broncode zijn gedeclareerd. Zie .NET Class Library voor meer informatie.
Een van de eerste beslissingen die u neemt bij het definiëren van een type is bepalen welke constructie u voor uw type wilt gebruiken. De volgende lijst helpt bij het nemen van die eerste beslissing. De keuzes overlappen elkaar. In de meeste scenario's is meer dan één optie een redelijke keuze.
- Als de grootte van de gegevensopslag klein is, niet meer dan 64 bytes, kiest u een
struct
ofrecord struct
. - Als het type onveranderbaar is of als u een niet-destructieve mutatie wilt, kiest u een
struct
ofrecord struct
. - Als uw type waardesemantiek moet hebben voor gelijkheid, kiest u een
record class
ofrecord struct
. - Als het type voornamelijk wordt gebruikt voor het opslaan van gegevens, niet voor gedrag, kiest u een
record class
ofrecord struct
. - Als het type deel uitmaakt van een overnamehiërarchie, kiest u een
record class
of eenclass
. - Als het type polymorfisme gebruikt, kiest u een
class
. - Als het primaire doel gedrag is, kiest u een
class
.
Het algemene typesysteem
Het is belangrijk om twee fundamentele punten over het typesysteem in .NET te begrijpen:
- Het ondersteunt het overnameprincipe. Typen kunnen worden afgeleid van andere typen, genaamd basistypen. Het afgeleide type neemt (met enkele beperkingen) de methoden, eigenschappen en andere leden van het basistype over. Het basistype kan op zijn beurt worden afgeleid van een ander type. In dat geval neemt het afgeleide type de leden van beide basistypen over in de overnamehiërarchie. Alle typen, met inbegrip van ingebouwde numerieke typen, zoals System.Int32 (C#-trefwoord:
int
), leiden uiteindelijk af van één basistype, namelijk System.Object (C#-trefwoord:object
). Deze uniforme typehiërarchie wordt het Common Type System (CTS) genoemd. Zie Overname voor meer informatie over overname in C#. - Elk type in de CTS wordt gedefinieerd als een waardetype of een verwijzingstype. Deze typen omvatten alle aangepaste typen in de .NET-klassebibliotheek en ook uw eigen door de gebruiker gedefinieerde typen. Typen die u definieert met behulp van het
struct
trefwoord zijn waardetypen; alle ingebouwde numerieke typen zijnstructs
. Typen die u definieert met behulp van hetclass
ofrecord
trefwoord, zijn verwijzingstypen. Referentietypen en waardetypen hebben verschillende compileertijdregels en verschillende runtimegedrag.
In de volgende afbeelding ziet u de relatie tussen waardetypen en verwijzingstypen in de CTS.
Opmerking
U kunt zien dat de meest gebruikte typen allemaal zijn ingedeeld in de System naamruimte. De naamruimte waarin een type zich bevindt, heeft echter geen relatie met het feit of het een waardetype of verwijzingstype is.
Klassen en structs zijn twee van de basisconstructies van het algemene typesysteem in .NET. Elk is in feite een gegevensstructuur die een set gegevens en gedragingen inkapselt die bij elkaar horen als een logische eenheid. De gegevens en het gedrag zijn de leden van de klasse, struct of record. De leden omvatten methoden, eigenschappen, gebeurtenissen, enzovoort, zoals later in dit artikel vermeld.
Een klasse-, struct- of recorddeclaratie is vergelijkbaar met een blauwdruk die wordt gebruikt voor het maken van exemplaren of objecten tijdens runtime. Als u een klasse, struct of record met de naam Person
definieert, Person
is dit de naam van het type. Als u een variabele declareert en initialiseert p
van het type Person
, wordt p
als een object of exemplaar van Person
beschouwd. Meerdere exemplaren van hetzelfde Person
type kunnen worden gemaakt en elk exemplaar kan verschillende waarden hebben in de eigenschappen en velden.
Een klasse is een verwijzingstype. Wanneer een object van het type wordt gemaakt, bevat de variabele waaraan het object wordt toegewezen alleen een verwijzing naar dat geheugen. Wanneer de objectverwijzing is toegewezen aan een nieuwe variabele, verwijst de nieuwe variabele naar het oorspronkelijke object. Wijzigingen die via één variabele worden aangebracht, worden doorgevoerd in de andere variabele, omdat ze beide verwijzen naar dezelfde gegevens.
Een struct is een waardetype. Wanneer een struct wordt gemaakt, bevat de variabele waaraan de struct wordt toegewezen de werkelijke gegevens van de struct. Wanneer de struct is toegewezen aan een nieuwe variabele, wordt deze gekopieerd. De nieuwe variabele en de oorspronkelijke variabele bevatten daarom twee afzonderlijke kopieën van dezelfde gegevens. Wijzigingen die in de ene kopie zijn aangebracht, hebben geen invloed op de andere kopie.
Recordtypen kunnen verwijzingstypen (record class
) of waardetypen (record struct
) zijn. Recordtypen bevatten methoden die ondersteuning bieden voor gelijkheid van waarden.
In het algemeen worden klassen gebruikt om complexer gedrag te modelleren. Klassen slaan doorgaans gegevens op die zijn bedoeld om te worden gewijzigd nadat een klasseobject is gemaakt. Structs zijn het meest geschikt voor kleine gegevensstructuren. Structs slaan doorgaans gegevens op die niet zijn bedoeld om te worden gewijzigd nadat de struct is gemaakt. Recordtypen zijn gegevensstructuren met extra gesynthetiseerde compilerleden. Records slaan doorgaans gegevens op die niet zijn bedoeld om te worden gewijzigd nadat het object is gemaakt.
Waardetypen
Waardetypen zijn afgeleid van System.ValueType, dat is afgeleid van System.Object. Typen die zijn afgeleid van System.ValueType hebben speciaal gedrag in de CLR. Variabelen van het waardetype bevatten rechtstreeks hun waarden. Het geheugen voor een struct wordt inline toegewezen in welke context de variabele ook wordt gedeclareerd. Er is geen afzonderlijke heap-toewijzing of afvalverzameling-overhead voor waardetype-variabelen. U kunt typen declareren record struct
die waardetypen zijn en de gesynthetiseerde leden voor records opnemen.
Er zijn twee categorieën waardetypen: struct
en enum
.
De ingebouwde numerieke typen zijn structs en ze hebben velden en methoden waartoe u toegang hebt:
// constant field on type byte.
byte b = byte.MaxValue;
Maar u declareert en wijst waarden toe alsof het eenvoudige niet-geaggregeerde typen zijn:
byte num = 0xA;
int i = 5;
char c = 'Z';
Waardetypen zijn gesloten. U kunt een type niet afleiden van een waardetype, bijvoorbeeld System.Int32. U kunt geen struct definiëren die moet worden overgenomen van een door de gebruiker gedefinieerde klasse of struct, omdat een struct alleen kan overnemen van System.ValueType. Een struct kan echter een of meer interfaces implementeren. U kunt een structtype omzetten naar elk interfacetype dat het implementeert. Deze cast zorgt ervoor dat een boxing-operatie de struct verpakt in een object van het verwijzingstype op de beheerde heap. Boxing-operaties vinden plaats wanneer u een waardetype doorgeeft aan een methode die een System.Object of een interfacetype als invoerparameter neemt. Zie Boxing and Unboxing (Boksen en Uitpakken) voor meer informatie.
U gebruikt het struct-trefwoord om uw eigen aangepaste waardetypen te maken. Normaal gesproken wordt een struct gebruikt als een container voor een kleine set gerelateerde variabelen, zoals wordt weergegeven in het volgende voorbeeld:
public struct Coords
{
public int x, y;
public Coords(int p1, int p2)
{
x = p1;
y = p2;
}
}
Zie Structuurtypen voor meer informatie over structs. Zie Waardetypen voor meer informatie over waardetypen.
De andere categorie van waardetypen is enum
. Een enum definieert een set benoemde integrale constanten. De System.IO.FileMode opsomming in de .NET-klassebibliotheek bevat bijvoorbeeld een set benoemde constante gehele getallen die aangeven hoe een bestand moet worden geopend. Deze is gedefinieerd zoals wordt weergegeven in het volgende voorbeeld:
public enum FileMode
{
CreateNew = 1,
Create = 2,
Open = 3,
OpenOrCreate = 4,
Truncate = 5,
Append = 6,
}
De System.IO.FileMode.Create constante heeft een waarde van 2. De naam is echter veel zinvoller voor mensen die de broncode lezen en daarom is het beter om opsommingen te gebruiken in plaats van constante letterlijke getallen. Zie System.IO.FileMode voor meer informatie.
Alle opsommingen erven van System.Enum, dat erft van System.ValueType. Alle regels die van toepassing zijn op structs, zijn ook van toepassing op opsommingen. Zie Opsommingstypen voor meer informatie over opsommingen.
Referentietypen
Een type dat is gedefinieerd als een class
, record
, delegate
, array, of interface
is een reference type
.
Wanneer u een variabele van een reference type
variabele declareert, bevat deze de waarde null
totdat u deze toewijst met een exemplaar van dat type of een variabele maakt met behulp van de new
operator. In het volgende voorbeeld wordt het maken en toewijzen van een klasse gedemonstreerd:
MyClass myClass = new MyClass();
MyClass myClass2 = myClass;
Een interface
kan niet rechtstreeks worden geïnstantieerd met behulp van de new
operator. Maak en wijs in plaats daarvan een exemplaar van een klasse toe waarmee de interface wordt geïmplementeerd. Bekijk het volgende voorbeeld:
MyClass myClass = new MyClass();
// Declare and assign using an existing value.
IMyInterface myInterface = myClass;
// Or create and assign a value in a single statement.
IMyInterface myInterface2 = new MyClass();
Wanneer het object wordt gemaakt, wordt het geheugen toegewezen aan de beheerde heap. De variabele bevat alleen een verwijzing naar de locatie van het object. Typen op de beheerde heap vereisen een overhead zowel bij toewijzing als bij vrijgave. Garbagecollection is de automatische geheugenbeheerfunctionaliteit van de CLR, die de reclamatie uitvoert. Garbage collection is echter ook zeer geoptimaliseerd en in de meeste scenario's ontstaat er geen prestatieprobleem. Zie Automatisch geheugenbeheer voor meer informatie over garbagecollection.
Alle matrices zijn verwijzingstypen, zelfs als hun elementen waardetypen zijn. Matrices zijn impliciet afgeleid van de System.Array klasse. U declareert en gebruikt deze met de vereenvoudigde syntaxis die wordt geleverd door C#, zoals wordt weergegeven in het volgende voorbeeld:
// Declare and initialize an array of integers.
int[] nums = [1, 2, 3, 4, 5];
// Access an instance property of System.Array.
int len = nums.Length;
Referentietypes bieden volledige ondersteuning voor inheritantie. Wanneer u een klasse maakt, kunt u overnemen van een andere interface of klasse die niet is gedefinieerd als verzegeld. Andere klassen kunnen overnemen van uw klas en uw virtuele methoden overschrijven. Zie Klassen, structs en records voor meer informatie over het maken van uw eigen klassen. Zie Overname voor meer informatie over overname en virtuele methoden.
Typen letterlijke waarden
In C# ontvangen letterlijke waarden een type van de compiler. U kunt opgeven hoe een numerieke letterlijke waarde moet worden getypt door een letter toe te voegen aan het einde van het getal. Als u bijvoorbeeld wilt opgeven dat de waarde 4.56
moet worden behandeld als een float
, voegt u een "f" of "F" toe na het getal: 4.56f
. Als er geen letter wordt toegevoegd, wordt door de compiler een type voor de letterlijke tekst afgeleid. Zie Integrale numerieke typen en numerieke drijvende kommatypen voor meer informatie over welke typen kunnen worden opgegeven met letterachtervoegsels.
Omdat letterlijke waarden worden getypt en alle typen uiteindelijk worden afgeleid van System.Object, kunt u code schrijven en compileren, zoals de volgende code:
string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);
Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);
Algemene typen
Een type kan worden gedeclareerd met een of meer typeparameters die fungeren als tijdelijke aanduiding voor het werkelijke type (het betontype). Clientcode biedt het concrete type wanneer er een exemplaar van het type wordt gemaakt. Dergelijke typen worden algemene typen genoemd. Het .NET-type System.Collections.Generic.List<T> heeft bijvoorbeeld één typeparameter die volgens conventie de naam T
krijgt. Wanneer u een exemplaar van het type maakt, geeft u het type op van de objecten die de lijst bevat, string
bijvoorbeeld:
List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);
Het gebruik van de typeparameter maakt het mogelijk om dezelfde klasse opnieuw te gebruiken om elk type element te bevatten, zonder dat elk element naar object hoeft te worden geconverteerd. Algemene verzamelingsklassen worden sterk getypte verzamelingen genoemd omdat de compiler het specifieke type elementen van de verzameling kent en een fout kan veroorzaken tijdens het compileren als u bijvoorbeeld probeert een geheel getal toe te voegen aan het object in het stringList
vorige voorbeeld. Zie Generics voor meer informatie.
Impliciete typen, anonieme typen en null-waarde typen
U kunt impliciet een lokale variabele (maar geen klasseleden) typen met behulp van het var
trefwoord. De variabele ontvangt nog steeds een type tijdens het compileren, maar het type wordt geleverd door de compiler. Zie Impliciet getypte lokale variabelen voor meer informatie.
Het kan lastig zijn om een benoemd type te maken voor eenvoudige sets gerelateerde waarden die u niet van plan bent om buiten de methodegrenzen op te slaan of door te geven. U kunt hiervoor anonieme typen maken. Zie Anonieme typen voor meer informatie.
Gewone waardetypen kunnen geen waarde hebben van null
. U kunt echter null-waardetypen maken door een ?
na het type toe te voegen. Is bijvoorbeeld int?
een int
type dat ook de waarde null
kan hebben. Nulleerbare waardetypen zijn exemplaren van het generieke structuurtype System.Nullable<T>. Nullable waarde-typen zijn vooral handig wanneer u gegevens naar en vanuit databases doorgeeft waarin numerieke waarden mogelijk null
zijn. Zie Nullable-waardetypen voor meer informatie.
Type compileertijd en runtime-type
Een variabele kan verschillende typen compileertijd en runtime hebben. Het type compileertijd is het gedeclareerde of afgeleid type van de variabele in de broncode. Het runtimetype is het type van het exemplaar waarnaar wordt verwezen door die variabele. Vaak zijn deze twee typen hetzelfde, zoals in het volgende voorbeeld:
string message = "This is a string of characters";
In andere gevallen is het type compileertijd anders, zoals wordt weergegeven in de volgende twee voorbeelden:
object anotherMessage = "This is another string of characters";
IEnumerable<char> someCharacters = "abcdefghijklmnopqrstuvwxyz";
In beide voorgaande voorbeelden is het runtimetype een string
. Het type compileertijd bevindt zich object
op de eerste regel en IEnumerable<char>
in de tweede regel.
Als de twee typen verschillen voor een variabele, is het belangrijk om te begrijpen wanneer het type compileertijd en het runtimetype van toepassing zijn. Het type compileertijd bepaalt alle acties die door de compiler worden uitgevoerd. Deze compileracties omvatten methode-aanroepresolutie, overbelastingsresolutie en beschikbare impliciete en expliciete casts. Het uitvoeringstype bepaalt alle acties die tijdens de uitvoering zijn opgelost. Deze runtimeacties omvatten het verzenden van virtuele methode-aanroepen, evalueren is
en switch
expressies en andere type test-API's. Als u beter wilt weten hoe uw code communiceert met typen, herkent u welke actie van toepassing is op welk type.
Verwante secties
Zie de volgende artikelen voor meer informatie:
C#-taalspecificatie
Zie de C#-taalspecificatie voor meer informatie. De taalspecificatie is de definitieve bron voor de C#-syntaxis en het gebruik.