16 Structs
16.1 Algemeen
Structs zijn vergelijkbaar met klassen omdat ze gegevensstructuren vertegenwoordigen die gegevensleden en functieleden kunnen bevatten. In tegenstelling tot klassen zijn structs echter waardetypen en hoeven heaptoewijzing niet te worden toegewezen. Een variabele van een struct
type bevat rechtstreeks de gegevens van de struct
, terwijl een variabele van een klassetype een verwijzing naar de gegevens bevat, de laatste ook wel een object genoemd.
Opmerking: Structs zijn met name handig voor kleine gegevensstructuren die waardesemantiek hebben. Complexe getallen, punten in een coördinatensysteem of sleutel-waardeparen in een woordenlijst zijn allemaal goede voorbeelden van structs. De sleutel voor deze gegevensstructuren is dat ze weinig gegevensleden hebben, dat ze geen gebruik hoeven te maken van overname- of verwijzingsemantiek, maar dat ze handig kunnen worden geïmplementeerd met behulp van waardesemantiek waarbij de toewijzing de waarde kopieert in plaats van de verwijzing. eindnotitie
Zoals beschreven in §8.3.5 zijn de eenvoudige typen van C#, zoals int
, double
en bool
, eigenlijk alle structtypen.
16.2 Struct-declaraties
16.2.1 Algemeen
Een struct_declaration is een type_declaration (§14.7) die een nieuwe struct declareert:
struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
Een struct_declaration bestaat uit een optionele set kenmerken (§22), gevolgd door een optionele set struct_modifiers (§16.2.2), gevolgd door een optionele ref
wijzigingsfunctie (§16.2.3), gevolgd door een optionele gedeeltelijke wijziging (§15.2.7), gevolgd door het trefwoord struct
en een id die de struct een naam geeft, gevolgd door een optionele type_parameter_list specificatie (§15.2.3), gevolgd door een optionele struct_interfaces specificatie (§16.2.5), gevolgd door een optionele specificatie van type_parameter_constraints-componenten (§15.2.5), gevolgd door een struct_body (§16.2.6), eventueel gevolgd door een puntkomma.
Een structverklaring levert geen type_parameter_constraints_clauses , tenzij zij ook een type_parameter_list levert.
Een struct-verklaring die een type_parameter_list levert, is een algemene structdeclaratie. Bovendien is elke struct die genest is in een algemene klassedeclaratie of een algemene structdeclaratie zelf een algemene struct-declaratie, aangezien typeargumenten voor het betreffende type worden opgegeven om een geconstrueerd type te maken (§8.4).
Een structverklaring met een ref
trefwoord mag geen struct_interfaces deel hebben.
16.2.2 Struct modifiers
Een struct_declaration kan eventueel een reeks struct_modifier sbevatten:
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§23.2) is alleen beschikbaar in onveilige code (§23).
Het is een compilatiefout voor dezelfde wijziging die meerdere keren in een struct-declaratie wordt weergegeven.
Behalve, readonly
de modifiers van een structdeclaratie hebben dezelfde betekenis als die van een klassedeclaratie (§15.2.2).
De readonly
wijzigingsfunctie geeft aan dat de struct_declaration een type declareert waarvan exemplaren onveranderbaar zijn.
Een alleen-lezen struct heeft de volgende beperkingen:
- Elk van de instantievelden wordt ook gedeclareerd
readonly
. - Geen van de exemplaareigenschappen heeft een set_accessor_declaration (§15.7.3).
- Zij declareert geen veldachtige gebeurtenissen (§15.8.2).
Wanneer een exemplaar van een readonly-struct wordt doorgegeven aan een methode, this
wordt het behandeld als een invoerargument/parameter, waardoor schrijftoegang tot alle exemplaarvelden wordt toegestemd (behalve door constructors).
16.2.3 Ref modifier
De ref
wijzigingsfunctie geeft aan dat de struct_declaration een type declareert waarvan exemplaren worden toegewezen aan de uitvoeringsstack. Deze typen worden verw-structtypen genoemd. De ref
wijzigingsfunctie verklaart dat instanties ref-achtige velden mogen bevatten en worden niet uit de veilige context gekopieerd (§16.4.12). De regels voor het bepalen van de veilige context van een refstruct worden beschreven in §16.4.12.
Dit is een compilatiefout als een verwijzingstype wordt gebruikt in een van de volgende contexten:
- Als het elementtype van een matrix.
- Als het gedeclareerde type van een veld van een klasse of een struct die niet over de
ref
wijzigingsfunctie beschikt. - Wordt in het vak geplaatst op
System.ValueType
ofSystem.Object
. - Als typeargument.
- Als het type tuple-element.
- Een asynchrone methode.
- Een iterator.
- Er is geen conversie van een
ref struct
type naar het typeobject
of het typeSystem.ValueType
. - Een
ref struct
type wordt niet gedeclareerd om een interface te implementeren. - Een instantiemethode die in
object
of inSystem.ValueType
eenref struct
type wordt gedeclareerd, maar niet wordt overschreven, wordt niet aangeroepen met een ontvanger van datref struct
type. - Een exemplaarmethode van een
ref struct
type wordt niet vastgelegd door methodegroepconversie naar een gemachtigde. - Een verw-struct mag niet worden vastgelegd door een lambda-expressie of een lokale functie.
Opmerking: A
ref struct
declareertasync
geen exemplaarmethoden en gebruikt geen ofyield return
yield break
instructie binnen een instantiemethode, omdat de implicietethis
parameter niet kan worden gebruikt in deze contexten. eindnotitie
Deze beperkingen zorgen ervoor dat een variabele van het ref struct
type niet verwijst naar stackgeheugen dat niet langer geldig is of op variabelen die niet langer geldig zijn.
16.2.4 Gedeeltelijke wijziging
De partial
wijzigingsfunctie geeft aan dat deze struct_declaration een gedeeltelijke typedeclaratie is. Meerdere gedeeltelijke struct-declaraties met dezelfde naam binnen een tussenliggende naamruimte of typedeclaratie combineren tot één struct-declaratie, volgens de regels die zijn opgegeven in §15.2.7.
16.2.5 Struct-interfaces
Een structdeclaratie kan een struct_interfaces specificatie bevatten. In dat geval wordt gezegd dat de struct rechtstreeks de opgegeven interfacetypen implementeert. Voor een samengesteld structtype, met inbegrip van een geneste type dat is gedeclareerd in een algemene typedeclaratie (§15.3.9.7), wordt elk geïmplementeerd interfacetype verkregen door voor elke type_parameter in de opgegeven interface de bijbehorende type_argument van het samengestelde type te vervangen.
struct_interfaces
: ':' interface_type_list
;
De verwerking van interfaces op meerdere delen van een gedeeltelijke structdeclaratie (§15.2.7) wordt verder besproken in §15.2.4.3.
Interface-implementaties worden verder besproken in §18.6.
16.2.6 Struct body
De struct_body van een struct definieert de leden van de struct.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Struct-leden
De leden van een struct bestaan uit de leden die zijn geïntroduceerd door de struct_member_declarations en de leden die zijn overgenomen van het type System.ValueType
.
struct_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| static_constructor_declaration
| type_declaration
| fixed_size_buffer_declaration // unsafe code support
;
fixed_size_buffer_declaration (§23.8.2) is alleen beschikbaar in onveilige code (§23).
Opmerking: alle soorten class_member_declarations behalve finalizer_declaration zijn ook struct_member_declarations. eindnotitie
Behalve de verschillen die in §16.4 zijn vermeld, gelden ook de beschrijvingen van klasseleden die in §15.3 tot en met §15.12 zijn vermeld voor structleden.
16.4 Klassen- en structverschillen
16.4.1 Algemeen
Structs verschillen op verschillende belangrijke manieren van klassen:
- Structs zijn waardetypen (§16.4.2).
- Alle structtypen nemen impliciet over van de klasse
System.ValueType
(§16.4.3). - Bij toewijzing aan een variabele van een structtype wordt een kopie gemaakt van de waarde die wordt toegewezen (§16.4.4).
- De standaardwaarde van een struct is de waarde die wordt geproduceerd door alle velden in te stellen op de standaardwaarde (§16.4.5).
- Boksen en uitpakken worden gebruikt om te converteren tussen een structtype en bepaalde verwijzingstypen (§16.4.6).
- De betekenis van
this
is anders binnen structleden (§16.4.7). - Declaraties van instantievelden voor een struct mogen geen variabele initializers bevatten (§16.4.8).
- Een struct is niet toegestaan om een constructor voor een parameterloze instantie te declareren (§16.4.9).
- Een struct is niet toegestaan om een finalizer te declareren.
16.4.2 Waardesemantiek
Structs zijn waardetypen (§8.3) en worden gezegd dat ze waardesemantiek hebben. Klassen zijn daarentegen verwijzingstypen (§8.2) en worden gezegd dat ze verwijzingssemantiek hebben.
Een variabele van een structtype bevat rechtstreeks de gegevens van de struct, terwijl een variabele van een klassetype een verwijzing bevat naar een object dat de gegevens bevat. Wanneer een struct B
een exemplaarveld van het type A
bevat en A
een structtype is, is het een compilatiefout die A
afhankelijk is van B
of een type dat is samengesteld uit B
. Een struct-X
is rechtstreeks afhankelijk van een struct-Y
als X
een exemplaarveld van het type Y
bevat. Gezien deze definitie is de volledige set structs waarop een struct afhankelijk is van de transitieve sluiting van de struct, rechtstreeks afhankelijk van de relatie.
Voorbeeld:
struct Node { int data; Node next; // error, Node directly depends on itself }
is een fout omdat
Node
het een exemplaarveld van een eigen type bevat. Nog een voorbeeldstruct A { B b; } struct B { C c; } struct C { A a; }
is een fout omdat elk van de typen
A
,B
enC
afhankelijk is van elkaar.eindvoorbeeld
Met klassen is het mogelijk dat twee variabelen verwijzen naar hetzelfde object en dus mogelijk zijn voor bewerkingen op één variabele die van invloed zijn op het object waarnaar wordt verwezen door de andere variabele. Met structs hebben de variabelen elk hun eigen kopie van de gegevens (behalve in het geval van by-reference-parameters) en is het niet mogelijk om bewerkingen op de ene te beïnvloeden. Behalve wanneer expliciet nullable (§8.3.12) is het bovendien niet mogelijk voor waarden van een structtype te zijn null
.
Opmerking: Als een struct een verwijzingsveld bevat, kan de inhoud van het object waarnaar wordt verwezen, worden gewijzigd door andere bewerkingen. De waarde van het veld zelf, d.w.w., welk object het verwijst, kan echter niet worden gewijzigd door een mutatie van een andere structwaarde. eindnotitie
Voorbeeld: Op basis van het volgende
struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point a = new Point(10, 10); Point b = a; a.x = 100; Console.WriteLine(b.x); } }
de uitvoer is
10
. De toewijzing vana
het makenb
van een kopie van de waarde enb
wordt dus niet beïnvloed door de toewijzing aana.x
. WasPoint
in plaats daarvan gedeclareerd als een klasse, zou de uitvoer zijn100
omdata
enb
zou verwijzen naar hetzelfde object.eindvoorbeeld
16.4.3 Overname
Alle structtypen nemen impliciet over van de klasse System.ValueType
, die op zijn beurt overgenomen worden van klasse object
. Een struct-declaratie kan een lijst met geïmplementeerde interfaces opgeven, maar het is niet mogelijk voor een struct-declaratie om een basisklasse op te geven.
Struct-typen zijn nooit abstract en worden altijd impliciet verzegeld. De abstract
en sealed
modifiers zijn daarom niet toegestaan in een structdeclaratie.
Aangezien overname niet wordt ondersteund voor structs, kan de gedeclareerde toegankelijkheid van een structlid niet protected
, private protected
of protected internal
.
Functieleden in een struct kunnen niet abstract of virtueel zijn en de override
modifier mag alleen methoden overschrijven die zijn overgenomen van System.ValueType
.
16.4.4 Toewijzing
Als toewijzing aan een variabele van een structtype wordt een kopie gemaakt van de waarde die wordt toegewezen. Dit verschilt van toewijzing tot een variabele van een klassetype, waarmee de verwijzing wordt gekopieerd, maar niet het object dat door de verwijzing wordt geïdentificeerd.
Net als bij een toewijzing wordt een struct doorgegeven als een waardeparameter of geretourneerd als resultaat van een functielid, wordt er een kopie van de struct gemaakt. Een struct kan worden doorgegeven door verwijzing naar een functielid met behulp van een by-reference-parameter.
Wanneer een eigenschap of indexeerfunctie van een struct het doel is van een toewijzing, wordt de exemplaarexpressie die is gekoppeld aan de toegang tot de eigenschap of indexeerfunctie geclassificeerd als een variabele. Als de exemplaarexpressie is geclassificeerd als een waarde, treedt er een compilatietijdfout op. Dit wordt nader beschreven in §12.21.2.
16.4.5 Standaardwaarden
Zoals beschreven in §9.3 worden verschillende soorten variabelen automatisch geïnitialiseerd naar hun standaardwaarde wanneer ze worden gemaakt. Voor variabelen van klassetypen en andere referentietypen is null
deze standaardwaarde. Omdat structs echter waardetypen zijn die niet kunnen zijn null
, is de standaardwaarde van een struct de waarde die wordt geproduceerd door alle waardetypevelden in te stellen op de standaardwaarde en alle verwijzingstypevelden op null
.
Voorbeeld: Verwijzen naar de
Point
hierboven gedeclareerde struct, het voorbeeldPoint[] a = new Point[100];
initialiseert elk
Point
in de matrix op de waarde die wordt geproduceerd door dex
eny
velden in te stellen op nul.eindvoorbeeld
De standaardwaarde van een struct komt overeen met de waarde die wordt geretourneerd door de standaardconstructor van de struct (§8.3.3). In tegenstelling tot een klasse is een struct niet toegestaan om een constructor voor een parameterloze instantie te declareren. In plaats daarvan heeft elke struct impliciet een constructor voor een exemplaar zonder parameter, die altijd de waarde retourneert die het resultaat is van het instellen van alle velden op de standaardwaarden.
Opmerking: Structs moeten worden ontworpen om rekening te houden met de standaard initialisatiestatus een geldige status. In het voorbeeld
struct KeyValuePair { string key; string value; public KeyValuePair(string key, string value) { if (key == null || value == null) { throw new ArgumentException(); } this.key = key; this.value = value; } }
de door de gebruiker gedefinieerde exemplaarconstructor beschermt zich alleen tegen
null
waarden waar deze expliciet wordt aangeroepen. In gevallen waarin eenKeyValuePair
variabele onderhevig is aan standaardwaarde-initialisatie, zijnkey
de velden envalue
veldennull
en moet de struct worden voorbereid om deze status te verwerken.eindnotitie
16.4.6 Boksen en uitpakken
Een waarde van een klassetype kan worden geconverteerd naar een type object
of naar een interfacetype dat door de klasse wordt geïmplementeerd door de verwijzing als een ander type tijdens het compileren te behandelen. Op dezelfde manier kan een waarde van het type object
of een waarde van een interfacetype worden geconverteerd naar een klassetype zonder de verwijzing te wijzigen (maar in dit geval is een controle van het runtime-type vereist).
Omdat structs geen verwijzingstypen zijn, worden deze bewerkingen anders geïmplementeerd voor structtypen. Wanneer een waarde van een structtype wordt geconverteerd naar bepaalde referentietypen (zoals gedefinieerd in §10.2.9), vindt er een boksbewerking plaats. Ook wanneer een waarde van bepaalde verwijzingstypen (zoals gedefinieerd in §10.3.7) wordt teruggezet naar een structtype, vindt er een uitboxbewerking plaats. Een belangrijk verschil met dezelfde bewerkingen voor klassetypen is dat met boksen en uitpakken de structwaarde naar of uit het boxed-exemplaar wordt gekopieerd.
Opmerking: Na een boksen- of uitboxbewerking worden wijzigingen die zijn aangebracht in het postvak
struct
UIT niet weergegeven in het vakstruct
. eindnotitie
Zie §10.2.9 en §10.3.7 voor meer informatie over boksen en uitpakken.
16.4.7 Betekenis van dit
De betekenis van this
een struct verschilt van de betekenis van this
een klasse, zoals beschreven in §12.8.14. Wanneer een structtype een virtuele methode overschrijft die is System.ValueType
overgenomen van (zoals Equals
, GetHashCode
of ToString
), wordt de aanroep van de virtuele methode via een exemplaar van het structtype niet veroorzaakt door boksen. Dit geldt zelfs wanneer de struct wordt gebruikt als een typeparameter en de aanroep plaatsvindt via een exemplaar van het type parametertype.
Voorbeeld:
struct Counter { int value; public override string ToString() { value++; return value.ToString(); } } class Program { static void Test<T>() where T : new() { T x = new T(); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); } static void Main() => Test<Counter>(); }
De uitvoer van het programma is:
1 2 3
Hoewel het een slechte stijl is om
ToString
bijwerkingen te hebben, toont het voorbeeld aan dat er geen boksen heeft plaatsgevonden voor de drie aanroepen vanx.ToString()
.eindvoorbeeld
Op dezelfde manier treedt boksen nooit impliciet op bij het openen van een lid op een parameter van een beperkt type wanneer het lid wordt geïmplementeerd binnen het waardetype. Stel dat een interface ICounter
een methode Increment
bevat, die kan worden gebruikt om een waarde te wijzigen. Als ICounter
deze wordt gebruikt als een beperking, wordt de implementatie van de Increment
methode aangeroepen met een verwijzing naar de variabele die Increment
is aangeroepen, nooit een vakkenkopie.
Voorbeeld:
interface ICounter { void Increment(); } struct Counter : ICounter { int value; public override string ToString() => value.ToString(); void ICounter.Increment() => value++; } class Program { static void Test<T>() where T : ICounter, new() { T x = new T(); Console.WriteLine(x); x.Increment(); // Modify x Console.WriteLine(x); ((ICounter)x).Increment(); // Modify boxed copy of x Console.WriteLine(x); } static void Main() => Test<Counter>(); }
De eerste aanroep om de waarde in de variabele
Increment
tex
wijzigen. Dit is niet gelijk aan de tweede aanroep vanIncrement
, die de waarde wijzigt in een vakkenkopie vanx
. De uitvoer van het programma is dus:0 1 1
eindvoorbeeld
16.4.8 Veld initializers
Zoals beschreven in §16.4.5, bestaat de standaardwaarde van een struct uit de waarde die het resultaat is van het instellen van alle waardetypevelden op de standaardwaarde en alle verwijzingstypevelden op null
. Daarom staat een struct niet toe dat declaraties van instantievelden variabele initializers bevatten. Deze beperking is alleen van toepassing op exemplaarvelden. Statische velden van een struct mogen variabele initializers bevatten.
Voorbeeld: Het volgende
struct Point { public int x = 1; // Error, initializer not permitted public int y = 1; // Error, initializer not permitted }
is in de fout omdat de declaraties van het exemplaarveld variabelen initializers bevatten.
eindvoorbeeld
16.4.9 Constructors
In tegenstelling tot een klasse is een struct niet toegestaan om een constructor voor een parameterloze instantie te declareren. In plaats daarvan heeft elke struct impliciet een constructor voor een exemplaar zonder parameter, die altijd de waarde retourneert die het resultaat is van het instellen van alle waardetypevelden op de standaardwaarde en alle verwijzingstypevelden op null
(§8.3.3). Een struct kan exemplaarconstructors met parameters declareren.
Voorbeeld: Op basis van het volgende
struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point p1 = new Point(); Point p2 = new Point(0, 0); } }
de instructies maken zowel een met
Point
alsx
y
geïnitialiseerd naar nul.eindvoorbeeld
Een constructor voor het struct-exemplaar mag geen initialisatiefunctie voor de constructor van het formulier base(
argument_list)
opnemen, waarbij argument_list optioneel is.
De this
parameter van een struct-exemplaarconstructor komt overeen met een uitvoerparameter van het structtype. Als zodanig this
wordt zeker (§9.4) toegewezen op elke locatie waar de constructor terugkeert. Op dezelfde manier kan het niet worden gelezen (zelfs impliciet) in de constructorbody voordat deze definitief wordt toegewezen.
Als de constructor van het struct-exemplaar een constructor-initialisatiefunctie opgeeft, wordt deze initialisatiefunctie beschouwd als een definitieve toewijzing die plaatsvindt vóór de hoofdtekst van de constructor. Daarom heeft het lichaam zelf geen initialisatievereisten.
Voorbeeld: Bekijk de implementatie van de instantieconstructor hieronder:
struct Point { int x, y; public int X { set { x = value; } } public int Y { set { y = value; } } public Point(int x, int y) { X = x; // error, this is not yet definitely assigned Y = y; // error, this is not yet definitely assigned } }
Er kan geen exemplaarfunctielid (inclusief de settoegangsors voor de eigenschappen
X
enY
) worden aangeroepen totdat alle velden van de struct die worden samengesteld, zeker zijn toegewezen. Houd er echter rekening mee dat alsPoint
dit een klasse is in plaats van een struct, de instantieconstructor-implementatie is toegestaan. Er is één uitzondering op dit en dat omvat automatisch geïmplementeerde eigenschappen (§15.7.4). De definitieve toewijzingsregels (§12.21.2) die specifiek zijn vrijgesteld van toewijzing aan een automatische eigenschap van een structtype binnen een instantieconstructor van dat structtype: een dergelijke toewijzing wordt beschouwd als een definitieve toewijzing van het verborgen backingveld van de automatische eigenschap. Het volgende is dus toegestaan:struct Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) { X = x; // allowed, definitely assigns backing field Y = y; // allowed, definitely assigns backing field } }
eindvoorbeeld]
16.4.10 Statische constructors
Statische constructors voor structs volgen de meeste van dezelfde regels als voor klassen. De uitvoering van een statische constructor voor een structtype wordt geactiveerd door de eerste van de volgende gebeurtenissen in een toepassingsdomein:
- Er wordt naar een statisch lid van het structtype verwezen.
- Een expliciet gedeclareerde constructor van het structtype wordt aangeroepen.
Opmerking: Het maken van standaardwaarden (§16.4.5) van structtypen activeert de statische constructor niet. (Een voorbeeld hiervan is de oorspronkelijke waarde van elementen in een matrix.) eindnotitie
16.4.11 Eigenschappen automatisch geïmplementeerd
Automatisch geïmplementeerde eigenschappen (§15.7.4) maken gebruik van verborgen backingvelden, die alleen toegankelijk zijn voor de eigenschapstoegangsors.
Opmerking: Deze toegangsbeperking betekent dat constructors in structs met automatisch geïmplementeerde eigenschappen vaak een expliciete constructor-initialisatiefunctie nodig hebben waar ze anders geen behoefte aan hebben, om te voldoen aan de vereiste dat alle velden definitief worden toegewezen voordat een functielid wordt aangeroepen of de constructor retourneert. eindnotitie
16.4.12 Veilige contextbeperking
16.4.12.1 Algemeen
Tijdens het compileren wordt elke expressie gekoppeld aan een context waarin dat exemplaar en alle velden veilig kunnen worden geopend, de veilige context. De veilige context is een context waarin een expressie wordt geplaatst, die veilig is voor de waarde om te ontsnappen.
Elke expressie waarvan het type compileertijd geen refstruct is, heeft een veilige context van de aanroepercontext.
Een default
expressie heeft voor elk type een veilige context van de aanroepercontext.
Voor elke niet-standaardexpressie waarvan het type compileertijd een refstruct is, is een veilige context gedefinieerd door de volgende secties.
De veilige contextrecords waarnaar een waarde kan worden gekopieerd. Gezien een toewijzing van een expressie E1
met een veilige context S1
, naar een expressie E2
met een veilige context S2
, is het een fout als S2
dit een bredere context is dan S1
.
Er zijn drie verschillende veilige contextwaarden, hetzelfde als de ref-safe-contextwaarden die zijn gedefinieerd voor referentievariabelen (§9.7.2): declaratieblok, functielid en aanroepercontext. De veilige context van een expressie beperkt het gebruik ervan als volgt:
- Voor een retourverklaring
return e1
is de veilige context vane1
de aanroepercontext. - Voor een toewijzing
e1 = e2
is de veilige context vane2
ten minste zo breed als de veilige context vane1
.
Voor een methode-aanroep als er een ref
of out
argument van een ref struct
type is (inclusief de ontvanger, tenzij het type is readonly
), met een veilige context S1
, kan er geen argument (inclusief de ontvanger) een beperktere veilige context hebben dan S1
.
16.4.12.2 Parameter veilige context
Een parameter van een verwijzingstype, inclusief de this
parameter van een exemplaarmethode, heeft een veilige context van de aanroepercontext.
16.4.12.3 Lokale variabele veilige context
Een lokale variabele van een verwijzingstype heeft als volgt een veilige context:
- Als de variabele een iteratievariabele van een
foreach
lus is, is de veilige context van de variabele hetzelfde als de veilige context van de expressie van deforeach
lus. - Als de declaratie van de variabele een initialisatiefunctie heeft, is de veilige context van de variabele hetzelfde als de veilige context van die initialisatiefunctie.
- Anders wordt de variabele niet geïnitialiseerd op het moment van declaratie en heeft deze een veilige context van de aanroepercontext.
16.4.12.4 Veld veilige context
Een verwijzing naar een veld e.F
, waarbij het type F
een verwijzingstype is, heeft een veilige context die hetzelfde is als de veilige context van e
.
16.4.12.5 Operators
De toepassing van een door de gebruiker gedefinieerde operator wordt behandeld als een methodeaanroep (§16.4.12.6).
Voor een operator die een waarde oplevert, zoals e1 + e2
of c ? e1 : e2
, is de veilige context van het resultaat de kleinste context tussen de veilige contexten van de operanden van de operator. Als gevolg hiervan is voor een unaire operator die een waarde oplevert, zoals +e
, de veilige context van het resultaat de veilige context van de operand is.
Opmerking: De eerste operand van een voorwaardelijke operator is een
bool
, dus de veilige context is de aanroepercontext. Hierna volgt dat de resulterende veilige context de kleinste veilige context van de tweede en derde operand is. eindnotitie
16.4.12.6 Methode en eigenschap aanroepen
Een waarde die het gevolg is van een aanroep van een methode of eigenschapsinvocatie e1.M(e2, ...)
e.P
, heeft een veilige context van de kleinste van de volgende contexten:
- bellercontext.
- De veilige context van alle argumentexpressies (inclusief de ontvanger).
Een aanroep van eigenschappen (een of get
set
) wordt behandeld als een methode-aanroep van de onderliggende methode door de bovenstaande regels.
16.4.12.7 stackalloc
Het resultaat van een stackalloc-expressie heeft een veilige context van functielid.
16.4.12.8 Constructor aanroepen
Een new
expressie die een constructor aanroept, voldoet aan dezelfde regels als een methode-aanroep die wordt beschouwd om het type te retourneren dat wordt samengesteld.
Daarnaast is de veilige context het kleinste van de veilige contexten van alle argumenten en operanden van alle object initializer-expressies, recursief, als er een initialisatiefunctie aanwezig is.
Opmerking: Deze regels zijn afhankelijk
Span<T>
van het niet hebben van een constructor van de volgende vorm:public Span<T>(ref T p)
Een dergelijke constructor maakt exemplaren van
Span<T>
gebruikt als velden die niet kunnen worden onderscheiden van eenref
veld. De veiligheidsregels die in dit document worden beschreven, zijn afhankelijk vanref
velden die geen geldige constructie in C# of .NET zijn. eindnotitie
ECMA C# draft specification