16 structs
16.1 Allmänt
Structs liknar klasser eftersom de representerar datastrukturer som kan innehålla datamedlemmar och funktionsmedlemmar. Men till skillnad från klasser är structs värdetyper och kräver inte heap-allokering. En variabel av en struct
typ innehåller direkt data för struct
, medan en variabel av en klasstyp innehåller en referens till data, den senare kallas för ett objekt.
Obs! Structs är särskilt användbara för små datastrukturer som har värdesemantik. Komplexa tal, punkter i ett koordinatsystem eller nyckel/värde-par i en ordlista är alla bra exempel på structs. Nyckeln till dessa datastrukturer är att de har få datamedlemmar, att de inte kräver användning av arvs- eller referenssemantik, snarare kan de implementeras bekvämt med hjälp av värdesemantik där tilldelningen kopierar värdet i stället för referensen. slutkommentar
Enligt beskrivningen i §8.3.5 är de enkla typer som tillhandahålls av C#, som int
, double
och bool
, i själva verket alla structtyper.
16.2 Struct-deklarationer
16.2.1 Allmänt
En struct_declaration är en type_declaration (§14.7) som deklarerar en ny struct:
struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
En struct_declaration består av en valfri uppsättning attribut (§22), följt av en valfri uppsättning struct_modifier(§16.2.2), följt av en valfri ref
modifierare (§16.2.3), följt av en valfri partiell modifierare (§15.2.7), följt av nyckelordet struct
och en identifierare som namnger struct, följt av en valfri type_parameter_list specifikation (§15.2.3), följt av en valfri struct_interfaces specifikation (§16.2.5), följt av en valfri type_parameter_constraints-klausulsspecifikation (§15.2.5), följt av en struct_body (§16.2.6), eventuellt följt av semikolon.
En structdeklaration får inte tillhandahålla en type_parameter_constraints_clauses såvida den inte även tillhandahåller en type_parameter_list.
En struct-deklaration som tillhandahåller en type_parameter_list är en allmän structdeklaration. Dessutom är varje struct kapslad inuti en generisk klassdeklaration eller en generisk structdeklaration i sig en generisk structdeklaration, eftersom typargument för den innehållande typen ska levereras för att skapa en konstruerad typ (§8.4).
En struct-deklaration som innehåller ett ref
nyckelord får inte ha en struct_interfaces del.
16.2.2 Struct-modifierare
En struct_declaration kan eventuellt innehålla en sekvens med struct_modifiers:
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§23.2) är endast tillgänglig i osäker kod (§23).
Det är ett kompileringsfel för samma modifierare som visas flera gånger i en struct-deklaration.
Med undantag för readonly
har modifierarna för en structdeklaration samma betydelse som en klassdeklaration (§15.2.2).
Modifieraren readonly
anger att struct_declaration deklarerar en typ vars instanser är oföränderliga.
En skrivskyddad struct har följande begränsningar:
- Vart och ett av dess instansfält ska också deklareras
readonly
. - Ingen av dess instansegenskaper får ha en set_accessor_declaration (§15.7.3).
- Den skall inte deklarera några fältliknande händelser (§15.8.2).
När en instans av en skrivskyddad struct skickas till en metod behandlas den this
som ett indataargument/parameter, vilket inte tillåter skrivåtkomst till instansfält (förutom konstruktorer).
16.2.3 Refmodifierare
Modifieraren ref
anger att struct_declaration deklarerar en typ vars instanser allokeras i körningsstacken. Dessa typer kallas referensstruktureringstyper. Modifieraren ref
deklarerar att instanser kan innehålla ref-liknande fält och får inte kopieras ur sin säkra kontext (§16.4.12). Reglerna för att fastställa en referens structs säkra kontext beskrivs i §16.4.12.
Det är ett kompileringsfel om en referens struct-typ används i någon av följande kontexter:
- Som elementtyp för en matris.
- Som den deklarerade typen av ett fält i en klass eller en struct som inte har
ref
modifieraren. - Boxas till
System.ValueType
ellerSystem.Object
. - Som ett typargument.
- Som typ av ett tupppelelement.
- En asynkron metod.
- En iterator.
- Det finns ingen konvertering från en
ref struct
typ till typenobject
eller typenSystem.ValueType
. - En
ref struct
typ får inte deklareras för att implementera något gränssnitt. - En instansmetod som deklareras i
object
eller iSystem.ValueType
men som inte åsidosätts i enref struct
typ ska inte anropas med en mottagare av denref struct
typen. - En instansmetod av en
ref struct
typ ska inte samlas in av metodgruppskonvertering till en ombudstyp. - En referens struct får inte fångas upp av ett lambda-uttryck eller en lokal funktion.
Obs! A
ref struct
får inte deklareraasync
instansmetoder eller använda en elleryield break
-yield return
instruktion i en instansmetod, eftersom den implicitathis
parametern inte kan användas i dessa kontexter. slutkommentar
Dessa begränsningar säkerställer att en variabel av ref struct
typen inte refererar till stackminne som inte längre är giltigt eller till variabler som inte längre är giltiga.
16.2.4 Partiell modifierare
Modifieraren partial
anger att den här struct_declaration är en partiell typdeklaration. Flera partiella structdeklarationer med samma namn inom ett omslutande namnområde eller typdeklaration kombineras för att bilda en structdeklaration, enligt de regler som anges i §15.2.7.
16.2.5 Struct-gränssnitt
En structdeklaration kan innehålla en struct_interfaces specifikation, i vilket fall structen sägs direkt implementera de angivna gränssnittstyperna. För en konstruerad structtyp, inklusive en kapslad typ som deklarerats inom en allmän typdeklaration (§15.3.9.7), erhålls varje implementerad gränssnittstyp genom att ersätta, för varje type_parameter i det angivna gränssnittet, motsvarande type_argument av den konstruerade typen.
struct_interfaces
: ':' interface_type_list
;
Hanteringen av gränssnitt på flera delar av en partiell structdeklaration (§15.2.7) diskuteras ytterligare i §15.2.4.3.
Gränssnittsimplementeringar diskuteras ytterligare i §18.6.
16.2.6 Struct brödtext
Struct_body för en struct definierar medlemmarna i structen.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Struct medlemmar
Medlemmarna i en struct består av de medlemmar som introduceras av dess struct_member_declarations och de medlemmar som ärvts från typen 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) är endast tillgänglig i osäker kod (§23).
Obs! Alla typer av class_member_declarationutom finalizer_declaration är också struct_member_declarations. slutkommentar
Förutom de skillnader som anges i §16.4 gäller även beskrivningarna av klassmedlemmar som anges i §15.3 till och med §15.12 för structmedlemmar.
16.4 Skillnader i klass och struct
16.4.1 Allmänt
Structs skiljer sig från klasser på flera viktiga sätt:
- Structs är värdetyper (§16.4.2).
- Alla structtyper ärver implicit från klassen
System.ValueType
(§16.4.3). - Tilldelning till en variabel av en structtyp skapar en kopia av värdet som tilldelas (§16.4.4).
- Standardvärdet för en struct är det värde som genereras genom att ange alla fält till standardvärdet (§16.4.5).
- Boxnings- och avboxningsåtgärder används för att konvertera mellan en structtyp och vissa referenstyper (§16.4.6).
- Det menande av
this
är olikt inom structmedlemmar (§16.4.7). - Instansfältdeklarationer för en struct tillåts inte inkludera variabelinitierare (§16.4.8).
- En struct får inte deklarera en parameterlös instanskonstruktor (§16.4.9).
- En struct får inte deklarera en finalizer.
16.4.2 Värdesemantik
Structs är värdetyper (§8.3) och sägs ha värdesemantik. Klasser är däremot referenstyper (§8.2) och sägs ha referenssemantik.
En variabel av en structtyp innehåller direkt data för structen, medan en variabel av en klasstyp innehåller en referens till ett objekt som innehåller data. När en struct B
innehåller ett instansfält av typen A
och A
är en structtyp är det ett kompileringsfel för A
att vara beroende B
av eller en typ som har skapats från B
. A struct X
beror direkt på en struct Y
om X
innehåller ett instansfält av typen Y
. Med den här definitionen är den fullständiga uppsättningen structs som en struct är beroende av den transitiva stängningen av direkt beroende av relationen.
Exempel:
struct Node { int data; Node next; // error, Node directly depends on itself }
är ett fel eftersom
Node
det innehåller ett instansfält av sin egen typ. Ett annat exempelstruct A { B b; } struct B { C c; } struct C { A a; }
är ett fel eftersom var och en av typerna
A
,B
ochC
är beroende av varandra.slutexempel
Med klasser är det möjligt för två variabler att referera till samma objekt och därmed möjligt för åtgärder på en variabel att påverka objektet som refereras av den andra variabeln. Med structs har variablerna var och en sin egen kopia av data (förutom när det gäller bireferensparametrar), och det är inte möjligt för åtgärder på den ena att påverka den andra. Förutom när det uttryckligen är nullbart (§8.3.12) är det inte möjligt för värden av en structtyp att vara null
.
Obs! Om en struct innehåller ett fält av referenstyp kan innehållet i det refererade objektet ändras av andra åtgärder. Men värdet för själva fältet, dvs. vilket objekt det refererar till, kan inte ändras genom en mutation av ett annat structvärde. slutkommentar
Exempel: Med tanke på följande
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); } }
utdata är
10
. Tilldelningen ava
tillb
skapar en kopia av värdet ochb
påverkas därför inte av tilldelningen tilla.x
. Om utdata i stället hadePoint
deklarerats som en klass skulle det bero100
på atta
ochb
refererar till samma objekt.slutexempel
16.4.3 Arv
Alla structtyper ärver implicit från klassen System.ValueType
, som i sin tur ärver från klassen object
. En struct-deklaration kan ange en lista över implementerade gränssnitt, men det är inte möjligt för en struct-deklaration att ange en basklass.
Struct-typer är aldrig abstrakta och är alltid implicit förseglade. Modifierarna abstract
och sealed
tillåts därför inte i en struct-deklaration.
Eftersom arv inte stöds för structs kan den deklarerade tillgängligheten för en struct-medlem inte vara protected
, private protected
eller protected internal
.
Funktionsmedlemmar i en struct kan inte vara abstrakta eller virtuella, och override
modifieraren får endast åsidosätta metoder som ärvts från System.ValueType
.
16.4.4 Tilldelning
Tilldelning till en variabel av en structtyp skapar en kopia av värdet som tilldelas. Detta skiljer sig från tilldelning till en variabel av en klasstyp, som kopierar referensen men inte det objekt som identifieras av referensen.
Precis som en tilldelning skapas en kopia av structen när en struct skickas som en värdeparameter eller returneras som ett resultat av en funktionsmedlem. En struct kan skickas med referens till en funktionsmedlem med hjälp av en bireferensparameter.
När en egenskap eller indexerare för en struct är målet för en tilldelning ska instansuttrycket som är associerat med egenskapen eller indexerarens åtkomst klassificeras som en variabel. Om instansuttrycket klassificeras som ett värde uppstår ett kompileringsfel. Detta beskrivs närmare i §12.21.2.
16.4.5 Standardvärden
Enligt beskrivningen i §9.3 initieras flera typer av variabler automatiskt till standardvärdet när de skapas. För variabler av klasstyper och andra referenstyper är null
det här standardvärdet . Eftersom structs är värdetyper som inte kan vara null
, är standardvärdet för en struct det värde som genereras genom att ange alla värdetypsfält till standardvärdet och alla referenstypfält till null
.
Exempel: Med hänvisning till structen
Point
som deklarerats ovan, exempletPoint[] a = new Point[100];
initierar var
Point
och en i matrisen till det värde som skapas genom att fältenx
ochy
anges till noll.slutexempel
Standardvärdet för en struct motsvarar det värde som returneras av standardkonstruktorn för structen (§8.3.3). Till skillnad från en klass tillåts inte en struct deklarera en parameterlös instanskonstruktor. I stället har varje struct implicit en parameterlös instanskonstruktor, som alltid returnerar det värde som är resultatet av att ange alla fält till deras standardvärden.
Obs! Structs bör utformas för att betrakta standardinitieringstillståndet som ett giltigt tillstånd. I exemplet
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; } }
den användardefinierade instanskonstruktorn skyddar endast mot
null
värden där den uttryckligen anropas. I de fall där enKeyValuePair
variabel är föremål för standardvärdeinitiering blirnull
fältenkey
ochvalue
, och structen bör förberedas för att hantera det här tillståndet.slutkommentar
16.4.6 Boxning och avboxning
Ett värde av en klasstyp kan konverteras till typ object
eller till en gränssnittstyp som implementeras av klassen genom att bara behandla referensen som en annan typ vid kompileringstid. På samma sätt kan ett värde av typen object
eller ett värde av en gränssnittstyp konverteras tillbaka till en klasstyp utan att referensen ändras (men naturligtvis krävs en körningstypskontroll i det här fallet).
Eftersom structs inte är referenstyper implementeras dessa åtgärder på olika sätt för structtyper. När ett värde av en structtyp konverteras till vissa referenstyper (enligt definitionen i §10.2.9), sker en boxningsåtgärd. På samma sätt, när ett värde av vissa referenstyper (enligt definitionen i §10.3.7) konverteras tillbaka till en structtyp, sker en avboxningsåtgärd. En viktig skillnad jämfört med samma åtgärder för klasstyper är att boxning och avboxning kopierar structvärdet antingen till eller från den boxade instansen.
Obs! Efter en boxnings- eller avboxningsåtgärd återspeglas inte ändringar som görs i den oboxade
struct
i rutanstruct
. slutkommentar
Mer information om boxning och unboxing finns i §10.2.9 och §10.3.7.
16.4.7 Innebörden av detta
Innebörden av this
i en struct skiljer sig från innebörden av this
i en klass, enligt beskrivningen i §12.8.14. När en struct-typ åsidosätter en virtuell metod som ärvts från System.ValueType
(till exempel Equals
, GetHashCode
eller ToString
), orsakar anrop av den virtuella metoden via en instans av struct-typen inte boxning. Detta gäller även när struct används som en typparameter och anropet sker via en instans av typparametertypen.
Exempel:
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>(); }
Programmets utdata är:
1 2 3
Även om det är dålig stil för
ToString
att ha biverkningar, visar exemplet att ingen boxning inträffade för de tre anropen avx.ToString()
.slutexempel
På samma sätt sker boxning aldrig implicit vid åtkomst till en medlem på en begränsad typparameter när medlemmen implementeras inom värdetypen. Anta till exempel att ett gränssnitt ICounter
innehåller en metod Increment
, som kan användas för att ändra ett värde. Om ICounter
används som en begränsning anropas implementeringen av Increment
metoden med en referens till variabeln som Increment
anropades, aldrig en rutad kopia.
Exempel:
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>(); }
Det första anropet för att
Increment
ändra värdet i variabelnx
. Detta motsvarar inte det andra anropet tillIncrement
, som ändrar värdet i en rutad kopia avx
. Därför är programmets utdata:0 1 1
slutexempel
16.4.8 Fältinitierare
Enligt beskrivningen i §16.4.5 består standardvärdet för en struct av det värde som är resultatet av att ange alla värdetypsfält till deras standardvärde och alla referenstypfält till null
. Därför tillåter en struct inte instansfältdeklarationer att inkludera variabelinitierare. Den här begränsningen gäller endast för instansfält. Statiska fält i en struct tillåts inkludera variabelinitierare.
Exempel: Följande
struct Point { public int x = 1; // Error, initializer not permitted public int y = 1; // Error, initializer not permitted }
är ett fel eftersom instansfältdeklarationerna innehåller variabelinitierare.
slutexempel
16.4.9 Konstruktorer
Till skillnad från en klass tillåts inte en struct deklarera en parameterlös instanskonstruktor. I stället har varje struct implicit en parameterlös instanskonstruktor, som alltid returnerar det värde som är resultatet av att ange alla värdetypsfält till standardvärdet och alla referenstypfält till null
(§8.3.3). En struct kan deklarera att instanskonstruktorer har parametrar.
Exempel: Med tanke på följande
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); } }
-uttrycken skapar både en
Point
medx
ochy
initieras till noll.slutexempel
En struct-instanskonstruktor får inte innehålla en konstruktorinitierare av formuläret base(
argument_list)
, där argument_list är valfritt.
Parametern this
för en struct-instanskonstruktor motsvarar en utdataparameter av typen struct. this
Därför skall definitivt tilldelas (§9.4) på varje plats där konstruktorn returnerar. På samma sätt kan den inte läsas (även implicit) i konstruktorns brödtext innan den definitivt tilldelas.
Om struct-instanskonstruktorn anger en konstruktorinitierare anses initieraren vara en bestämd tilldelning till detta som inträffar före konstruktorns brödtext. Därför har själva brödtexten inga initieringskrav.
Exempel: Överväg implementeringen av instanskonstruktorn nedan:
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 } }
Ingen instansfunktionsmedlem (inklusive uppsättningsåtkomsterna för egenskaperna
X
ochY
) kan anropas förrän alla fält i den struct som skapas definitivt har tilldelats. Observera dock att omPoint
det var en klass i stället för en struct skulle instanskonstruktorimplementeringen tillåtas. Det finns ett undantag till detta och det omfattar automatiskt implementerade egenskaper (§15.7.4). De bestämda tilldelningsreglerna (§12.21.2) undantar specifikt tilldelning till en autoegenskap av en structtyp inom en instanskonstruktor av den typen: en sådan tilldelning anses vara en bestämd tilldelning av det dolda bakgrundsfältet för den automatiska egenskapen. Därför tillåts följande: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 } }
slutexempel]
16.4.10 Statiska konstruktorer
Statiska konstruktorer för structs följer de flesta av samma regler som för klasser. Körningen av en statisk konstruktor för en struct-typ utlöses av den första av följande händelser som inträffar inom en programdomän:
- En statisk medlem av structtypen refereras till.
- En explicit deklarerad konstruktor av typen struct anropas.
Obs! Skapandet av standardvärden (§16.4.5) av structtyper utlöser inte den statiska konstruktorn. (Ett exempel på detta är det initiala värdet för element i en matris.) slutkommentar
16.4.11 Automatiskt implementerade egenskaper
Automatiskt implementerade egenskaper (§15.7.4) använder dolda bakgrundsfält, som endast är tillgängliga för egenskapsåtkomsterna.
Obs! Den här åtkomstbegränsningen innebär att konstruktorer i structs som innehåller automatiskt implementerade egenskaper ofta behöver en explicit konstruktorinitierare där de annars inte skulle behöva en, för att uppfylla kravet på alla fält som definitivt tilldelas innan någon funktionsmedlem anropas eller konstruktorn returnerar. slutkommentar
16.4.12 Villkor för säker kontext
16.4.12.1 Allmänt
Vid kompilering är varje uttryck associerat med en kontext där den instansen och alla dess fält kan nås på ett säkert sätt, dess säkra kontext. Safe-context är en kontext som omsluter ett uttryck, vilket är säkert för värdet att fly till.
Alla uttryck vars kompileringstidstyp inte är en referens-struct har en säker kontext av anroparkontext.
Ett default
uttryck för alla typer har safe-context för anroparkontext.
För alla icke-standarduttryck vars kompileringstidstyp är en referensstruct har en safe-context definierad av följande avsnitt.
Safe-context registrerar vilken kontext ett värde kan kopieras till. Med en tilldelning från ett uttryck E1
med en safe-context S1
, till ett uttryck E2
med safe-context S2
, är det ett fel om S2
är en bredare kontext än S1
.
Det finns tre olika safe-context-värden, samma som referens-safe-context-värdena som definierats för referensvariabler (§9.7.2): declaration-block, function-member och caller-context. Ett uttrycks säkra kontext begränsar dess användning enligt följande:
- För ett returmeddelande
return e1
ska säkerhetskontextene1
för vara anroparkontext. - För en tilldelning
e1 = e2
ska säkerhetskontextene2
vara minst lika bred som säkerhetskontextene1
för .
För ett metodanrop om det finns ett ref
eller out
ett argument av en ref struct
typ (inklusive mottagaren om inte typen är readonly
), med safe-context S1
, kan inget argument (inklusive mottagaren) ha en smalare säker kontext än S1
.
16.4.12.2 Parametersäker kontext
En parameter av typen ref struct, inklusive parametern this
för en instansmetod, har en säker kontext av anroparkontext.
16.4.12.3 Säker kontext för lokal variabel
En lokal variabel av typen ref struct har en säker kontext enligt följande:
- Om variabeln är en iterationsvariabel för en
foreach
loop är variabelns safe-context samma som safe-context för loopensforeach
uttryck. - Om variabelns deklaration annars har en initiering är variabelns safe-context samma som initiatorns säkra kontext.
- I annat fall är variabeln ennitialiserad vid deklarationspunkten och har en säker kontext av anroparkontext.
16.4.12.4 Fältsäker kontext
En referens till ett fält e.F
, där typen av F
är en referens-struct-typ, har en säker kontext som är samma som safe-context för e
.
16.4.12.5 Operatorer
Tillämpningen av en användardefinierad operatör behandlas som ett metodanrop (§16.4.12.6).
För en operator som ger ett värde, till exempel e1 + e2
eller c ? e1 : e2
, är safe-context för resultatet den smalaste kontexten bland operatorns säkra kontexter. För en unary-operator som ger ett värde, till exempel +e
, är resultatets säkra kontext därför operandens säkra kontext.
Obs! Den första operanden för en villkorsoperator är en
bool
, så dess safe-context är caller-context. Av detta följer att den resulterande säkra kontexten är den smalaste säkra kontexten för den andra och tredje operanden. slutkommentar
16.4.12.6 Metod- och egenskapsanrop
Ett värde som härrör från en metodanrop e1.M(e2, ...)
eller egenskapsanrop e.P
har en säker kontext för den minsta av följande kontexter:
- anroparkontext.
- Safe-context för alla argumentuttryck (inklusive mottagaren).
Ett egenskapsanrop (antingen get
eller set
) behandlas som en metodanrop av den underliggande metoden av ovanstående regler.
16.4.12.7 stackalloc
Resultatet av ett stackalloc-uttryck har safe-context för function-member.
16.4.12.8 Konstruktoranrop
Ett new
uttryck som anropar en konstruktor följer samma regler som en metodanrop som anses returnera den typ som skapas.
Dessutom är safe-context den minsta av de säkra kontexterna för alla argument och operander för alla objektinitieringsuttryck, rekursivt, om någon initiator finns.
Obs! Dessa regler förlitar sig på
Span<T>
att inte ha någon konstruktor i följande formulär:public Span<T>(ref T p)
En sådan konstruktor gör att instanser av
Span<T>
används som fält som kan skiljas från ettref
fält. Säkerhetsreglerna som beskrivs i det här dokumentet beror på attref
fält inte är en giltig konstruktion i C# eller .NET. slutkommentar
ECMA C# draft specification