Strukturtyper (C#-referens)
En strukturtyp (eller structtyp) är en värdetyp som kan kapsla in data och relaterade funktioner. Du använder nyckelordet struct
för att definiera en strukturtyp:
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})";
}
Information om ref struct
och readonly ref struct
typer finns i artikeln referensstrukturtyper .
Strukturtyper har värdesemantik. En variabel av en strukturtyp innehåller alltså en instans av typen. Som standard kopieras variabelvärden vid tilldelning, skickar ett argument till en metod och returnerar ett metodresultat. För variabler av strukturtyp kopieras en instans av typen. Mer information finns i Värdetyper.
Vanligtvis använder du strukturtyper för att utforma små datacentrerade typer som ger lite eller inget beteende. .NET använder till exempel strukturtyper för att representera ett tal (både heltal och verkligt), ett booleskt värde, ett Unicode-tecken, en tidsinstans. Om du fokuserar på beteendet för en typ kan du överväga att definiera en klass. Klasstyper har referenssemantik. En variabel av en klasstyp innehåller alltså en referens till en instans av typen, inte själva instansen.
Eftersom strukturtyper har värdesemantik rekommenderar vi att du definierar oföränderliga strukturtyper.
readonly
Struct
Du använder readonly
modifieraren för att deklarera att en strukturtyp är oföränderlig. Alla datamedlemmar i en readonly
struct måste vara skrivskyddade enligt följande:
- Alla fältdeklarationer måste ha
readonly
modifieraren - Alla egenskaper, inklusive automatiskt implementerade, måste vara skrivskyddade eller
init
endast. Observera att init-only-setters endast är tillgängliga från C# version 9 och senare.
Det garanterar att ingen medlem i en readonly
struct ändrar structens tillstånd. Det innebär att andra instansmedlemmar utom konstruktorer implicit readonly
är .
Kommentar
I en readonly
struct kan en datamedlem av en föränderlig referenstyp fortfarande mutera sitt eget tillstånd. Du kan till exempel inte ersätta en List<T> instans, men du kan lägga till nya element i den.
Följande kod definierar en readonly
struct med init-only-egenskapsuppsättningar:
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
instansmedlemmar
Du kan också använda readonly
modifieraren för att deklarera att en instansmedlem inte ändrar tillståndet för en struct. Om du inte kan deklarera hela strukturtypen som readonly
använder du readonly
modifieraren för att markera de instansmedlemmar som inte ändrar struct-tillståndet.
I en readonly
instansmedlem kan du inte tilldela till strukturens instansfält. En medlem kan dock readonly
anropa en icke-medlemreadonly
. I så fall skapar kompilatorn en kopia av strukturinstansen och anropar den som intereadonly
är medlem på kopian. Därför ändras inte den ursprungliga strukturinstansen.
Vanligtvis tillämpar du modifieraren på readonly
följande typer av instansmedlemmar:
metoder:
public readonly double Sum() { return X + Y; }
Du kan också tillämpa modifieraren på
readonly
metoder som åsidosätter metoder som deklareras i System.Object:public readonly override string ToString() => $"({X}, {Y})";
egenskaper och indexerare:
private int counter; public int Counter { readonly get => counter; set => counter = value; }
Om du behöver tillämpa
readonly
modifieraren på båda åtkomstgivarna för en egenskap eller indexerare använder du den i deklarationen för egenskapen eller indexeraren.Kommentar
Kompilatorn deklarerar en
get
åtkomst till en automatiskt implementerad egenskap somreadonly
, oavsett förekomsten avreadonly
modifieraren i en egenskapsdeklaration.Du kan använda modifieraren för
readonly
en egenskap eller indexerare med eninit
accessor:public readonly double X { get; init; }
Du kan använda readonly
modifieraren för statiska fält av en strukturtyp, men inte andra statiska medlemmar, till exempel egenskaper eller metoder.
Kompilatorn kan använda readonly
modifieraren för prestandaoptimering. Mer information finns i Undvika allokeringar.
Icke-förstörande mutation
Från och med C# 10 kan du använda with
uttrycket för att skapa en kopia av en instans av strukturtyp med de angivna egenskaperna och fälten ändrade. Du använder syntaxen för objektinitieraren för att ange vilka medlemmar som ska ändras och deras nya värden, som följande exempel visar:
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
Från och med C# 10 kan du definiera poststrukturtyper. Posttyper ger inbyggda funktioner för att kapsla in data. Du kan definiera både record struct
och readonly record struct
typer. En post struct kan inte vara en ref struct
. Mer information och exempel finns i Poster.
Infogade matriser
Från och med C# 12 kan du deklarera infogade matriser som en struct
typ:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
private char _firstElement;
}
En infogad matris är en struktur som innehåller ett sammanhängande block med N-element av samma typ. Det är en motsvarighet till säker kod för den fasta buffertdeklarationen som endast är tillgänglig i osäker kod. En infogad matris är en struct
med följande egenskaper:
- Den innehåller ett enda fält.
- Struct anger inte någon explicit layout.
Dessutom validerar System.Runtime.CompilerServices.InlineArrayAttribute kompilatorn attributet:
- Längden måste vara större än noll (
> 0
). - Måltypen måste vara en struct.
I de flesta fall kan en infogad matris nås som en matris, både för att läsa och skriva värden. Dessutom kan du använda intervall- och indexoperatorerna.
Det finns minimala begränsningar för typen av det enskilda fältet i en infogad matris. Det kan inte vara en pekartyp:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
private unsafe char* _pointerElement; // CS9184
}
men det kan vara vilken referenstyp som helst eller vilken värdetyp som helst:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
private string _referenceElement;
}
Du kan använda infogade matriser med nästan vilken C#-datastruktur som helst.
Infogade matriser är en avancerad språkfunktion. De är avsedda för scenarier med höga prestanda där ett sammanhängande elementblock är snabbare än andra alternativa datastrukturer. Du kan lära dig mer om infogade matriser från funktionsspecifikationen
Struct-initiering och standardvärden
En variabel av en struct
typ innehåller direkt data för den struct
. Det skapar en skillnad mellan en ennitialiserad struct
, som har sitt standardvärde och en initierad struct
, som lagrar värden som anges genom att konstruera den. Tänk till exempel på följande kod:
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 ()
}
Som föregående exempel visar ignorerar standardvärdeuttrycket en parameterlös konstruktor och genererar standardvärdet för strukturtypen. Instansiering av matriser av strukturtyp ignorerar också en parameterlös konstruktor och skapar en matris som fylls i med standardvärdena för en strukturtyp.
Den vanligaste situationen där du ser standardvärden finns i matriser eller i andra samlingar där intern lagring innehåller block med variabler. I följande exempel skapas en matris med 30 TemperatureRange
strukturer som var och en har standardvärdet:
// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];
Alla en structs medlemsfält måste definitivt tilldelas när de skapas eftersom struct
typerna lagrar sina data direkt. Värdet default
för en struct har definitivt tilldelat alla fält till 0. Alla fält måste definitivt tilldelas när en konstruktor anropas. Du initierar fält med hjälp av följande mekanismer:
- Du kan lägga till fältinitierare i valfri fält- eller automatiskt implementerad egenskap.
- Du kan initiera alla fält eller automatiska egenskaper i konstruktorns brödtext.
Från och med C# 11 lägger kompilatorn till kod i konstruktorn som initierar dessa fält till standardvärdet om du inte initierar alla fält i en struct. Kompilatorn utför sin vanliga definitiva tilldelningsanalys. Alla fält som används innan de tilldelas eller definitivt inte tilldelas när konstruktorn har slutfört körningen tilldelas sina standardvärden innan konstruktorns brödtext körs. Om this
används innan alla fält tilldelas initieras structen till standardvärdet innan konstruktorns brödtext körs.
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 ()
}
Varje struct
konstruktor har en public
parameterlös konstruktor. Om du skriver en parameterlös konstruktor måste den vara offentlig. Om en struct deklarerar några fältinitierare måste den uttryckligen deklarera en konstruktor. Konstruktorn behöver inte vara parameterlös. Om en struct deklarerar en fältinitierare men inga konstruktorer rapporterar kompilatorn ett fel. Alla explicit deklarerade konstruktorer (med parametrar eller parameterlösa) kör alla fältinitierare för den structen. Alla fält utan fältinitierare eller tilldelning i en konstruktor är inställda på standardvärdet. Mer information finns i funktionsförslaget för parameterlösa structkonstruktorer .
Från och med C# 12 struct
kan typer definiera en primär konstruktor som en del av deklarationen. Primära konstruktorer ger en koncis syntax för konstruktorparametrar som kan användas i hela brödtexten struct
, i alla medlemsdeklarationer för den structen.
Om alla instansfält av en strukturtyp är tillgängliga kan du även instansiera det utan operatorn new
. I så fall måste du initiera alla instansfält innan den första användningen av instansen. I följande exempel visas hur du gör det:
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)
}
}
När det gäller de inbyggda värdetyperna använder du motsvarande literaler för att ange ett värde av typen.
Begränsningar med designen av en strukturtyp
Structs har de flesta funktionerna i en klasstyp . Det finns vissa undantag och vissa undantag som har tagits bort i senare versioner:
- En strukturtyp kan inte ärva från en annan klass eller strukturtyp och det kan inte vara basen för en klass. En strukturtyp kan dock implementera gränssnitt.
- Du kan inte deklarera en finalator inom en strukturtyp.
- Före C# 11 måste en konstruktor av en strukturtyp initiera alla instansfält av typen.
Skicka variabler av strukturtyp efter referens
När du skickar en variabel av strukturtyp till en metod som ett argument eller returnerar ett strukturtypvärde från en metod kopieras hela instansen av en strukturtyp. Genomströmningsvärde kan påverka kodens prestanda i scenarier med höga prestanda som omfattar stora strukturtyper. Du kan undvika värdekopiering genom att skicka en variabel av strukturtyp med referens. ref
Använd parametermodifierarna , out
, in
eller ref readonly
method för att ange att ett argument måste skickas med referens. Använd referensreturer för att returnera ett metodresultat efter referens. Mer information finns i Undvik allokeringar.
struct-villkor
Du använder också nyckelordet struct
i villkoret struct
för att ange att en typparameter är en värdetyp som inte kan null-värdet. Både struktur- och uppräkningstyper uppfyller villkoret struct
.
Omvandlingar
För alla strukturtyper (förutom ref struct
typer) finns det boxnings- och avboxningskonverteringar till och från typerna System.ValueType och System.Object . Det finns också boxnings- och avboxningskonverteringar mellan en strukturtyp och alla gränssnitt som implementeras.
Språkspecifikation för C#
Mer information finns i avsnittet Structs i C#-språkspecifikationen.
Mer information om struct
funktioner finns i följande kommentarer om funktionsförslag:
- Readonly structs
- Readonly-instansmedlemmar
- C# 10 – Parameterlösa structkonstruktorer
- C# 10 – Tillåt
with
uttryck på structs - C# 10 – Post structs
- C# 11 – Automatisk standard structs