Struktúratípusok (C#-referencia)
A struktúratípus (vagy struktúratípus) olyan értéktípus , amely képes adatokat és kapcsolódó funkciókat beágyazni. A kulcsszóval struct
definiálhat egy struktúratípust:
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})";
}
További információ ref struct
és readonly ref struct
típusok: ref structure types article.
A struktúratípusok értékszemantikával rendelkeznek. Vagyis egy struktúratípus változója a típus egy példányát tartalmazza. Alapértelmezés szerint a változóértékek másolása hozzárendeléskor történik, argumentumot ad át egy metódusnak, és visszaad egy metóduseredményt. A struktúra típusú változók esetében a rendszer kimásolja a típus egy példányát. További információ: Értéktípusok.
Általában olyan kis adatcentrikus típusokat tervez, amelyek nem vagy egyáltalán nem biztosítanak viselkedést. A .NET például struktúratípusokkal jelöl egy számot (egész és valós), logikai értéket, Unicode-karaktert és időpéldányt. Ha egy típus viselkedésére összpontosít, fontolja meg egy osztály definiálását. Az osztálytípusok referencia szemantikával rendelkeznek. Vagyis egy osztálytípus változója a típus egy példányára mutató hivatkozást tartalmaz, nem magát a példányt.
Mivel a struktúratípusok értékszemantikával rendelkeznek, javasoljuk, hogy definiáljon nem módosítható struktúratípusokat.
readonly
Struct
A módosító használatával readonly
deklarálhatja, hogy egy struktúratípus nem módosítható. A struktúra összes adattagjának readonly
írásvédettnek kell lennie az alábbiak szerint:
- Minden meződeklarációnak rendelkeznie kell a
readonly
módosítóval - Minden tulajdonságnak, beleértve az automatikusan végrehajtottakat is, írásvédettnek vagy
init
csak olvashatónak kell lennie. Vegye figyelembe, hogy a csak init-only setters csak a C# 9-es verziójától érhető el.
Ez garantálja, hogy a szerkezet egyik readonly
tagja sem módosítja a szerkezet állapotát. Ez azt jelenti, hogy a konstruktorok kivételével más példánytagok is implicit módon readonly
vannak.
Feljegyzés
readonly
Egy strukturált szerkezetben egy mutable referenciatípus adattagja továbbra is képes saját állapotát mutálni. Például nem cserélhet le egy példányt List<T> , de új elemeket adhat hozzá.
A következő kód egy readonly
csak init-only tulajdonsághalmazokkal rendelkező szerkezetet határoz meg:
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
példánytagok
A módosító használatával readonly
azt is deklarálhatja, hogy egy példánytag nem módosítja a szerkezet állapotát. Ha nem deklarálhatja a teljes struktúratípust, readonly
a readonly
módosítóval megjelölheti azokat a példánytagokat, amelyek nem módosítják a szerkezet állapotát.
readonly
Egy példánytagon belül nem rendelhető hozzá a struktúra példánymezőihez. Egy readonly
tag azonban meghívhat egy nem tagotreadonly
. Ebben az esetben a fordító létrehozza a struktúrapéldány egy példányát, és meghívja a nem tagotreadonly
a példányon. Ennek eredményeképpen az eredeti struktúrapéldány nem módosul.
A módosító általában a readonly
következő típusú példánytagokra vonatkozik:
Módszerek:
public readonly double Sum() { return X + Y; }
A módosító a
readonly
következőben System.Objectdeklarált metódusokat felülíró metódusokra is alkalmazható:public readonly override string ToString() => $"({X}, {Y})";
tulajdonságok és indexelők:
private int counter; public int Counter { readonly get => counter; set => counter = value; }
Ha egy tulajdonság vagy indexelő mindkét tartozékára alkalmaznia kell a
readonly
módosítót, alkalmazza azt a tulajdonság vagy indexelő deklarációjában.Feljegyzés
A fordító egy automatikusan implementált tulajdonság
readonly
tartozékát deklaráljaget
, függetlenül attól, hogy a módosító szerepel-e areadonly
tulajdonságdeklarációban.A módosítót egy tartozékkal rendelkező
init
tulajdonságra vagy indexelőre alkalmazhatjareadonly
:public readonly double X { get; init; }
A módosító egy readonly
struktúratípus statikus mezőire alkalmazható, más statikus tagokra, például tulajdonságokra vagy metódusokra nem.
A fordító a módosítót a teljesítményoptimalizáláshoz használhatja readonly
. További információt a foglalások elkerülése című témakörben talál.
Nem strukturált mutáció
A C# 10-től kezdődően a with
kifejezéssel másolatot készíthet egy struktúra típusú példányról a megadott tulajdonságok és mezők módosításával. Az objektum inicializáló szintaxisával megadhatja, hogy a tagok milyen módosításokat és új értékeket módosítsanak, ahogy az alábbi példa is mutatja:
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
A C# 10-től kezdve megadhatja a rekordstruktúra típusait. A rekordtípusok beépített funkciókat biztosítanak az adatok beágyazására. Mindkettőt record struct
és readonly record struct
típust is meghatározhatja. A rekordstruktúra nem lehet ref struct
. További információkért és példákért lásd a Rekordok című témakört.
Beágyazott tömbök
A C# 12-től kezdődően a beágyazott tömböket típusként struct
deklarálhatja:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
private char _firstElement;
}
A beágyazott tömb olyan struktúra, amely azonos típusú N elemek egybefüggő blokkját tartalmazza. Ez a rögzített pufferdeklaráció biztonságos kódjának megfelelője, amely csak nem biztonságos kódban érhető el. A beágyazott tömbök struct
a következő jellemzőkkel rendelkeznek:
- Egyetlen mezőt tartalmaz.
- A szerkezet nem határoz meg explicit elrendezést.
Emellett a fordító ellenőrzi az System.Runtime.CompilerServices.InlineArrayAttribute attribútumot:
- A hossznak nullánál (
> 0
) nagyobbnak kell lennie. - A céltípusnak egy szerkezetnek kell lennie.
A legtöbb esetben a beágyazott tömbök úgy érhetők el, mint egy tömb, mind az olvasási, mind az írási értékekhez. Emellett használhatja a tartomány - és index operátorokat is.
A beágyazott tömb egyetlen mezőjének típusára minimális korlátozások vonatkoznak. Nem lehet mutatótípus:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
private unsafe char* _pointerElement; // CS9184
}
de lehet bármilyen referenciatípus, vagy bármilyen értéktípus:
[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
private string _referenceElement;
}
A beágyazott tömbök szinte bármilyen C#-adatstruktúrával használhatók.
A beágyazott tömbök speciális nyelvi funkciók. Nagy teljesítményű forgatókönyvekhez készültek, ahol egy beágyazott, összefüggő elemblokk gyorsabb, mint más alternatív adatstruktúrák. A beágyazott tömbökről a funkciós specifikációból tudhat meg többet
Szerkezet inicializálása és alapértelmezett értékei
Egy típus változója struct
közvetlenül tartalmazza az ehhez struct
tartozó adatokat. Ez különbséget tesz egy nem inicializált struct
érték és egy inicializált struct
érték között, amely a létrehozásukkal tárolja a beállított értékeket. Vegyük például a következő kódot:
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 ()
}
Ahogy az előző példa is mutatja, az alapértelmezett értékkifejezés figyelmen kívül hagy egy paraméter nélküli konstruktort, és létrehozza a struktúratípus alapértelmezett értékét . A struktúra típusú tömbök példányosítása szintén figyelmen kívül hagy egy paraméter nélküli konstruktort, és létrehoz egy olyan tömböt, amely egy struktúratípus alapértelmezett értékeivel van feltöltve.
Az alapértelmezett értékek leggyakrabban tömbökben vagy más gyűjteményekben találhatók, ahol a belső tároló változóblokkokat tartalmaz. Az alábbi példa egy 30 TemperatureRange
struktúrából álló tömböt hoz létre, amelyek mindegyike az alapértelmezett értékkel rendelkezik:
// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];
Az összes tagmezőt mindenképpen hozzá kell rendelni a létrehozáskor, mert struct
a típusok közvetlenül tárolják az adataikat. A default
szerkezet értéke mindenképpen a 0-hoz rendelte az összes mezőt. Minden mezőt mindenképpen hozzá kell rendelni egy konstruktor meghívásakor. A mezők inicializálása az alábbi mechanizmusokkal történik:
- Bármilyen mező- vagy automatikusan implementált tulajdonsághoz hozzáadhat mező inicializálókat .
- A konstruktor törzsében bármilyen mezőt vagy automatikus tulajdonságot inicializálhat.
A C# 11-től kezdődően, ha nem inicializálja a szerkezet összes mezőjét, a fordító kódot ad hozzá a konstruktorhoz, amely inicializálja ezeket a mezőket az alapértelmezett értékhez. A fordító elvégzi a szokásos határozott hozzárendelés-elemzést. A konstruktor törzsének végrehajtása előtt minden olyan mező, amely a kiosztás előtt elérhető, vagy nem feltétlenül van hozzárendelve, amikor a konstruktor befejezi a végrehajtást, hozzárendeli az alapértelmezett értékeket. Ha this
a rendszer az összes mező hozzárendelése előtt hozzáfér, a szerkezet inicializálva lesz az alapértelmezett értékre a konstruktor törzsének végrehajtása előtt.
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 ()
}
Mindegyiknek struct
paraméter nélküli konstruktora van public
. Ha paraméter nélküli konstruktort ír, annak nyilvánosnak kell lennie. Ha egy szerkezet bármilyen mező inicializálót deklarál, explicit módon konstruktort kell deklarálnia. A konstruktornak nem kell paraméter nélkülinek lennie. Ha egy szerkezet deklarál egy mező inicializálót, de konstruktorokat nem, a fordító hibát jelez. Bármely explicit módon deklarált konstruktor (paraméterekkel vagy paraméter nélküliekkel) végrehajtja az adott struktúra összes mező inicializálóját. A mező inicializálója vagy a konstruktorban lévő hozzárendelés nélküli mezők az alapértelmezett értékre vannak beállítva. További információ: Paraméter nélküli szerkezetkonstruktorok funkciójavaslat megjegyzése.
A C# 12-től struct
kezdődően a típusok meghatározhatnak egy elsődleges konstruktort a deklaráció részeként. Az elsődleges konstruktorok a teljes törzsben használható konstruktorparaméterek tömör szintaxisát biztosítják az struct
adott szerkezet bármely tagdeklarációjában.
Ha egy struktúratípus összes példánymezője elérhető, az operátor nélkül is létrehozhatja őket new
. Ebben az esetben a példány első használata előtt inicializálnia kell az összes példánymezőt. Az alábbi példa bemutatja, hogyan teheti ezt meg:
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)
}
}
A beépített értéktípusok esetében a megfelelő literálokkal adja meg a típus értékét.
A struktúratípus kialakítására vonatkozó korlátozások
A szerkezetek az osztálytípus legtöbb képességével rendelkeznek. Vannak kivételek, és néhány kivétel, amelyeket a legutóbbi verziók eltávolítottak:
- Egy struktúratípus nem örökölhető más osztálytól vagy struktúratípustól, és nem lehet egy osztály alapja. A struktúratípusok azonban implementálhatnak interfészeket.
- Szerkezettípuson belül nem deklarálhat véglegesítőt .
- A C# 11 előtt egy struktúratípus konstruktorának inicializálnia kell a típus összes példánymezőit.
Struktúra típusú változók átadása hivatkozás alapján
Ha egy struktúra típusú változót argumentumként ad át egy metódusnak, vagy egy metódusból ad vissza egy struktúra típusú értéket, a rendszer átmásolja a struktúratípus teljes példányát. Az átengedés érték szerint befolyásolhatja a kód teljesítményét olyan nagy teljesítményű forgatókönyvekben, amelyek nagy szerkezettípusokat foglalnak magukban. Az értékmásolást elkerülheti egy struktúra típusú változó hivatkozással történő átadásával. ref
A , out
, in
, vagy ref readonly
metódus paraméter módosítóival jelezheti, hogy egy argumentumot hivatkozással kell átadni. A ref returnek használatával hivatkozással ad vissza egy metóduseredményt. További információ: Foglalások elkerülése.
struktúrakorlát
A kényszerben szereplő struct
struct
kulcsszóval azt is megadhatja, hogy egy típusparaméter nem null értékű típus-e. A struktúra és az enumerálási típusok egyaránt megfelelnek a kényszernek struct
.
Konverziók
Bármilyen struktúratípushoz (kivéve ref struct
a típusokat) léteznek boxing és unboxing konverziók az és System.Object a System.ValueType típusok között. Léteznek boxing és unboxing konverziók között egy struktúratípus és bármely felület, amelyet implementál.
C# nyelvspecifikáció
További információkért lásd a C#-nyelv specifikációjának Structs szakaszát.
A funkciókkal kapcsolatos struct
további információkért tekintse meg a következő funkciókra vonatkozó javaslati megjegyzéseket:
- Olvasható szerkezetek
- Olvasható példányok tagjai
- C# 10 – Paraméter nélküli szerkezetkonstruktorok
- C# 10 – Kifejezés engedélyezése
with
a szerkezeteken - C# 10 – Rekordstruktúra
- C# 11 – Automatikus alapértelmezett szerkezetek