Structuurtypen (C#-verwijzing)
Een structuurtype (of structtype) is een waardetype dat gegevens en gerelateerde functionaliteit kan inkapselen. U gebruikt het struct
trefwoord om een structuurtype te definiëren:
public struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; }
public double Y { get; }
public override string ToString() => $"({X}, {Y})";
}
Zie het artikel over ref-structuurtypen voor informatie over ref struct
en readonly ref struct
typen.
Structuurtypen hebben waardesemantiek. Dat wil gezegd: een variabele van een structuurtype bevat een exemplaar van het type. Standaard worden variabelewaarden gekopieerd bij de toewijzing, waarbij een argument aan een methode wordt doorgegeven en een methoderesultaat wordt geretourneerd. Voor structuurvariabelen wordt een exemplaar van het type gekopieerd. Zie Waardetypen voor meer informatie.
Normaal gesproken gebruikt u structuurtypen om kleine gegevensgerichte typen te ontwerpen die weinig of geen gedrag bieden. .NET gebruikt bijvoorbeeld structuurtypen om een getal (zowel geheel getal als reëel) weer te geven, een Booleaanse waarde, een Unicode-teken, een tijdexemplaren. Als u zich richt op het gedrag van een type, kunt u overwegen een klasse te definiëren. Klassetypen hebben verwijzingssemantiek. Dat wil gezegd: een variabele van een klassetype bevat een verwijzing naar een exemplaar van het type, niet naar het exemplaar zelf.
Omdat structuurtypen waardesemantiek hebben, raden we u aan onveranderbare structuurtypen te definiëren.
readonly
Struct
U gebruikt de readonly
wijzigingsfunctie om aan te geven dat een structuurtype onveranderbaar is. Alle gegevensleden van een readonly
struct moeten als volgt alleen-lezen zijn:
- Elke velddeclaratie moet de
readonly
wijzigingsfunctie hebben - Alle eigenschappen, inclusief automatisch geïmplementeerde eigenschappen, moeten alleen-lezen of
init
alleen-lezen zijn. Houd er rekening mee dat init-only setters alleen beschikbaar zijn vanaf C# versie 9.
Dat garandeert dat geen enkel lid van een readonly
struct de status van de struct wijzigt. Dit betekent dat andere exemplaarleden, behalve constructors, impliciet readonly
zijn.
Notitie
In een readonly
struct kan een gegevenslid van een onveranderbaar verwijzingstype nog steeds de eigen status muteren. U kunt bijvoorbeeld een List<T> exemplaar niet vervangen, maar u kunt er nieuwe elementen aan toevoegen.
De volgende code definieert een readonly
struct met init-only eigenschapssetters:
public readonly struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; init; }
public double Y { get; init; }
public override string ToString() => $"({X}, {Y})";
}
readonly
exemplaarleden
U kunt de readonly
wijzigingsfunctie ook gebruiken om aan te geven dat een exemplaarlid de status van een struct niet wijzigt. Als u het hele structuurtype niet kunt declareren als readonly
, gebruikt u de readonly
wijzigingsfunctie om de exemplaarleden te markeren die de status van de struct niet wijzigen.
Binnen een readonly
exemplaarlid kunt u niet toewijzen aan de instantievelden van de structuur. Een readonly
lid kan echter een niet-lidreadonly
aanroepen. In dat geval maakt de compiler een kopie van het structuurexemplaar en roept het niet-lidreadonly
op die kopie aan. Als gevolg hiervan wordt het oorspronkelijke structuurexemplaren niet gewijzigd.
Normaal gesproken past u de readonly
wijzigingsfunctie toe op de volgende soorten instantieleden:
Methoden:
public readonly double Sum() { return X + Y; }
U kunt de
readonly
wijzigingsfunctie ook toepassen op methoden die methoden overschrijven die zijn gedeclareerd in System.Object:public readonly override string ToString() => $"({X}, {Y})";
eigenschappen en indexeerfuncties:
private int counter; public int Counter { readonly get => counter; set => counter = value; }
Als u de
readonly
wijzigingsfunctie wilt toepassen op beide toegangsrechten van een eigenschap of indexeerfunctie, past u deze toe in de declaratie van de eigenschap of indexeerfunctie.Notitie
De compiler declareert een
get
toegangsfunctie van een automatisch geïmplementeerde eigenschap alsreadonly
, ongeacht de aanwezigheid van dereadonly
wijzigingsfunctie in een eigenschapsdeclaratie.U kunt de
readonly
wijzigingsfunctie toepassen op een eigenschap of indexeerfunctie met eeninit
toegangsfunctie:public readonly double X { get; init; }
U kunt de readonly
wijzigingsfunctie toepassen op statische velden van een structuurtype, maar niet op andere statische leden, zoals eigenschappen of methoden.
De compiler kan gebruikmaken van de readonly
wijzigingsfunctie voor prestatieoptimalisaties. Zie Toewijzingen vermijden voor meer informatie.
Niet-destructieve mutatie
Vanaf C# 10 kunt u de with
expressie gebruiken om een kopie van een exemplaar van het structuurtype te produceren met de opgegeven eigenschappen en velden die zijn gewijzigd. U gebruikt de syntaxis van de object-initialisatiefunctie om op te geven welke leden moeten worden gewijzigd en de nieuwe waarden, zoals in het volgende voorbeeld wordt weergegeven:
public readonly struct Coords
{
public Coords(double x, double y)
{
X = x;
Y = y;
}
public double X { get; init; }
public double Y { get; init; }
public override string ToString() => $"({X}, {Y})";
}
public static void Main()
{
var p1 = new Coords(0, 0);
Console.WriteLine(p1); // output: (0, 0)
var p2 = p1 with { X = 3 };
Console.WriteLine(p2); // output: (3, 0)
var p3 = p1 with { X = 1, Y = 4 };
Console.WriteLine(p3); // output: (1, 4)
}
record
Struct
Vanaf C# 10 kunt u recordstructuurtypen definiëren. Recordtypen bieden ingebouwde functionaliteit voor het inkapselen van gegevens. U kunt zowel record struct
readonly record struct
typen als typen definiëren. Een recordstruct kan geen ref struct
. Zie Records voor meer informatie en voorbeelden.
Inlinematrices
Vanaf C# 12 kunt u inlinematrices declareren als een struct
type:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
private char _firstElement;
}
Een inlinematrix is een structuur die een aaneengesloten blok N-elementen van hetzelfde type bevat. Het is een safe-code-equivalent van de vaste bufferdeclaratie die alleen beschikbaar is in onveilige code. Een inlinematrix is een struct
met de volgende kenmerken:
- Het bevat één veld.
- De struct geeft geen expliciete indeling op.
Daarnaast valideert de compiler het System.Runtime.CompilerServices.InlineArrayAttribute kenmerk:
- De lengte moet groter zijn dan nul (
> 0
). - Het doeltype moet een struct zijn.
In de meeste gevallen kan een inlinematrix worden geopend als een matrix, zowel om waarden te lezen als te schrijven. Daarnaast kunt u de bereik- en indexoperators gebruiken.
Er gelden minimale beperkingen voor het type enkel veld van een inlinematrix. Het kan geen aanwijzertype zijn:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
private unsafe char* _pointerElement; // CS9184
}
maar dit kan elk verwijzingstype of elk waardetype zijn:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
private string _referenceElement;
}
U kunt inlinematrices gebruiken met vrijwel elke C#-gegevensstructuur.
Inlinematrices zijn een geavanceerde taalfunctie. Ze zijn bedoeld voor scenario's met hoge prestaties waarbij een inline, aaneengesloten blok elementen sneller is dan andere alternatieve gegevensstructuren. Meer informatie over inlinematrices vindt u in de functiespeclet
Initialisatie en standaardwaarden voor struct
Een variabele van een struct
type bevat rechtstreeks de gegevens voor die struct
. Hiermee wordt een onderscheid gemaakt tussen een niet-geïnitialiseerde struct
, die de standaardwaarde en een geïnitialiseerde struct
waarde bevat, waarin waarden worden opgeslagen die zijn ingesteld door deze samen te stellen. Denk bijvoorbeeld aan de volgende code:
public readonly struct Measurement
{
public Measurement()
{
Value = double.NaN;
Description = "Undefined";
}
public Measurement(double value, string description)
{
Value = value;
Description = description;
}
public double Value { get; init; }
public string Description { get; init; }
public override string ToString() => $"{Value} ({Description})";
}
public static void Main()
{
var m1 = new Measurement();
Console.WriteLine(m1); // output: NaN (Undefined)
var m2 = default(Measurement);
Console.WriteLine(m2); // output: 0 ()
var ms = new Measurement[2];
Console.WriteLine(string.Join(", ", ms)); // output: 0 (), 0 ()
}
Zoals in het voorgaande voorbeeld wordt weergegeven, negeert de standaardwaardeexpressie een constructor zonder parameters en produceert de standaardwaarde van het structuurtype. Met een structuurtype-matrixexemptatie wordt ook een constructor zonder parameters genegeerd en wordt een matrix gegenereerd die is gevuld met de standaardwaarden van een structuurtype.
De meest voorkomende situatie waarin u standaardwaarden ziet, is in matrices of in andere verzamelingen waarin interne opslag blokken variabelen bevat. In het volgende voorbeeld wordt een matrix van 30 TemperatureRange
structuren gemaakt, die elk de standaardwaarde hebben:
// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];
Alle lidvelden van een struct moeten zeker worden toegewezen wanneer deze worden gemaakt, omdat struct
typen hun gegevens rechtstreeks opslaan. De default
waarde van een struct heeft zeker alle velden toegewezen aan 0. Alle velden moeten zeker worden toegewezen wanneer een constructor wordt aangeroepen. U initialiseert velden met behulp van de volgende mechanismen:
- U kunt veld initialisaties toevoegen aan een veld of automatisch geïmplementeerde eigenschap.
- U kunt velden of automatische eigenschappen initialiseren in de hoofdtekst van de constructor.
Vanaf C# 11 voegt de compiler code toe aan de constructor waarmee deze velden worden geïnitialiseerd naar de standaardwaarde als u niet alle velden in een struct initialiseert. De compiler voert de gebruikelijke definitieve toewijzingsanalyse uit. Velden die worden geopend voordat ze worden toegewezen, of niet zeker toegewezen wanneer de constructor klaar is met uitvoeren, worden de standaardwaarden toegewezen voordat de constructortekst wordt uitgevoerd. Als this
deze wordt geopend voordat alle velden worden toegewezen, wordt de struct geïnitialiseerd op de standaardwaarde voordat de hoofdtekst van de constructor wordt uitgevoerd.
public readonly struct Measurement
{
public Measurement(double value)
{
Value = value;
}
public Measurement(double value, string description)
{
Value = value;
Description = description;
}
public Measurement(string description)
{
Description = description;
}
public double Value { get; init; }
public string Description { get; init; } = "Ordinary measurement";
public override string ToString() => $"{Value} ({Description})";
}
public static void Main()
{
var m1 = new Measurement(5);
Console.WriteLine(m1); // output: 5 (Ordinary measurement)
var m2 = new Measurement();
Console.WriteLine(m2); // output: 0 ()
var m3 = default(Measurement);
Console.WriteLine(m3); // output: 0 ()
}
Elk struct
heeft een public
parameterloze constructor. Als u een parameterloze constructor schrijft, moet deze openbaar zijn. Als een struct veldinitiitiizers declareert, moet deze expliciet een constructor declareren. Deze constructor hoeft niet parameterloos te zijn. Als een struct een veld-initializer maar geen constructors declareert, rapporteert de compiler een fout. Elke expliciet gedeclareerde constructor (met parameters of parameterloos) voert alle veldinitiitiizers voor die struct uit. Alle velden zonder een veldinitiizer of een toewijzing in een constructor worden ingesteld op de standaardwaarde. Zie voor meer informatie de functievoorstelnotitie voor parameterloze struct constructors .
Vanaf C# 12 struct
kunnen typen een primaire constructor definiëren als onderdeel van de declaratie. Primaire constructors bieden een beknopte syntaxis voor constructorparameters die in de struct
hele hoofdtekst kunnen worden gebruikt, in elke liddeclaratie voor die struct.
Als alle instantievelden van een structuurtype toegankelijk zijn, kunt u deze ook instantiëren zonder de new
operator. In dat geval moet u alle exemplaarvelden initialiseren vóór het eerste gebruik van het exemplaar. In het volgende voorbeeld ziet u hoe u dit doet:
public static class StructWithoutNew
{
public struct Coords
{
public double x;
public double y;
}
public static void Main()
{
Coords p;
p.x = 3;
p.y = 4;
Console.WriteLine($"({p.x}, {p.y})"); // output: (3, 4)
}
}
In het geval van de ingebouwde waardetypen gebruikt u de bijbehorende letterlijke waarden om een waarde van het type op te geven.
Beperkingen met het ontwerp van een structuurtype
Structs hebben de meeste mogelijkheden van een klassetype . Er zijn enkele uitzonderingen en enkele uitzonderingen die zijn verwijderd in recentere versies:
- Een structuurtype kan niet overnemen van een ander klasse- of structuurtype en kan niet de basis van een klasse zijn. Een structuurtype kan echter interfaces implementeren.
- U kunt geen finalizer binnen een structuurtype declareren.
- Vóór C# 11 moet een constructor van een structuurtype alle exemplaarvelden van het type initialiseren.
Structuurvariabelen doorgeven per verwijzing
Wanneer u een structuurtypevariabele doorgeeft aan een methode als argument of een structuurtypewaarde retourneert van een methode, wordt het hele exemplaar van een structuurtype gekopieerd. Pass by-waarde kan van invloed zijn op de prestaties van uw code in scenario's met hoge prestaties die betrekking hebben op grote structuurtypen. U kunt het kopiëren van waarden voorkomen door een structuurvariabele door te geven op basis van verwijzing. Gebruik de ref
parameteraanpassingen , in
out
of ref readonly
methodeparameteraanpassingen om aan te geven dat een argument moet worden doorgegeven met verwijzing. Gebruik ref returns om een methoderesultaat te retourneren op basis van verwijzing. Zie Toewijzingen vermijden voor meer informatie.
structbeperking
U gebruikt ook het struct
trefwoord in de struct
beperking om op te geven dat een typeparameter een niet-null-waardetype is. Zowel structuur- als opsommingstypen voldoen aan de struct
beperking.
Conversies
Voor elk structuurtype (met uitzondering ref struct
van typen) bestaan boksen en het opheffen van conversies van en naar de System.ValueType en System.Object typen. Er bestaan ook conversies voor boksen en uitpakken tussen een structuurtype en elke interface die wordt geïmplementeerd.
C#-taalspecificatie
Zie de sectie Structs van de C#-taalspecificatie voor meer informatie.
Zie de volgende opmerkingen over functievoorstel voor meer informatie over struct
functies:
- Alleen-lezen structs
- Leden van een alleen-lezen exemplaar
- C# 10 - Parameterloze struct constructors
- C# 10 - Expressie toestaan
with
voor structs - C# 10 - Recordstructs
- C# 11 - Automatische standaardstructs