Delen via


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, doubleen 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, readonlyde 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 of System.Object.
  • Als typeargument.
  • Als het type tuple-element.
  • Een asynchrone methode.
  • Een iterator.
  • Er is geen conversie van een ref struct type naar het type object of het type System.ValueType.
  • Een ref struct type wordt niet gedeclareerd om een interface te implementeren.
  • Een instantiemethode die in object of in System.ValueType een ref struct type wordt gedeclareerd, maar niet wordt overschreven, wordt niet aangeroepen met een ontvanger van dat ref 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 declareert async geen exemplaarmethoden en gebruikt geen of yield returnyield break instructie binnen een instantiemethode, omdat de impliciete this 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-Xis rechtstreeks afhankelijk van een struct-Y als X een exemplaarveld van het type Ybevat. 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 voorbeeld

struct A { B b; }
struct B { C c; }
struct C { A a; }

is een fout omdat elk van de typen A, Ben C 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 van a het maken b van een kopie van de waarde en b wordt dus niet beïnvloed door de toewijzing aan a.x. Was Point in plaats daarvan gedeclareerd als een klasse, zou de uitvoer zijn 100 omdat a en b 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 protectedof 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 nulldeze 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 voorbeeld

Point[] a = new Point[100];

initialiseert elk Point in de matrix op de waarde die wordt geproduceerd door de x en y 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 een KeyValuePair variabele onderhevig is aan standaardwaarde-initialisatie, zijn keyde velden en value velden null 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 vak struct. 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, GetHashCodeof 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 van x.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 Incrementbevat, 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 Incrementte x wijzigen. Dit is niet gelijk aan de tweede aanroep van Increment, die de waarde wijzigt in een vakkenkopie van x. 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 als xy 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 en Y) worden aangeroepen totdat alle velden van de struct die worden samengesteld, zeker zijn toegewezen. Houd er echter rekening mee dat als Point 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 e1is de veilige context van e1 de aanroepercontext.
  • Voor een toewijzing e1 = e2 is de veilige context van e2 ten minste zo breed als de veilige context van e1.

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 de foreach 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 getset) 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 een ref veld. De veiligheidsregels die in dit document worden beschreven, zijn afhankelijk van ref velden die geen geldige constructie in C# of .NET zijn. eindnotitie