Megosztás a következőn keresztül:


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})";
}

Az ref struct és readonly ref struct típusokkal kapcsolatos információért lásd a struktúratípusok című cikket.

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 struktúratípusokat használunk kis adatcentrikus típusok tervezéséhez, amelyek kevés vagy egyáltalán semmilyen viselkedést nem biztosítanak. 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 readonly struktúrában az összes adattagnak írásvédettnek kell lennie az alábbiak szerint:

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 readonlyvannak.

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 List<T> példapéldányt, 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ány objektum tagok

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, readonlya 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 lehet hozzárendelni a struktúra példánymezőihez. Egy readonly tag azonban meghívhat egy nem-readonly tagot. 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.

Általában a következő típusú példánytagokra alkalmazza a readonly módosítót:

  • Módszerek:

    public readonly double Sum()
    {
        return X + Y;
    }
    

    Alkalmazhatja a readonly módosítót olyan metódusokra is, amelyek felülírják a System.Object-ben deklarált metódusokat.

    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 get hozzáférőt deklarál egy automatikusan implementált tulajdonsághoz readonly, függetlenül attól, hogy a readonly módosító szerepel-e a tulajdonság deklarációjában.

    A readonly módosítót egy init tartozékkal rendelkező tulajdonságra vagy indexelőre alkalmazhatja:

    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 teljesítményoptimalizáláshoz használhatja a readonly módosítót. További információt talál a foglalások elkerülése című témakörben.

Romboló hatás nélküli mutáció

A with kifejezés használatával 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át használja annak megadására, hogy mely tagokat kívánja módosítani és azok új értékeit, ahogy az alábbi példa 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

Megadhatja a rekordstruktúra típusait. A rekordtípusok beépített funkciókat biztosítanak az adatok beágyazására. Mind a record struct, mind a readonly record struct típust 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 inline tömböket típusként deklarálhatja:struct

[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. Egy beágyazott tömb struct a következő jellemzőkkel rendelkezik:

  • 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 soros tömb elérhető úgy, mint egy hagyományos tömb, mind az olvasáshoz, mind az íráshoz. 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.

Az inline tömbök haladó 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ócímű cikkből tudhat meg több információt.

Szerkezet inicializálása és alapértelmezett értékei

Egy típus változója struct közvetlenül tartalmazza az ehhez structtartozó adatokat. Ez különbséget tesz egy nem inicializált struct között, amely az alapértelmezett értékét tartalmazza, valamint egy inicializált struct között, amely az általa konfigurált értékeket tárolja. 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 létrehozásakor, mert struct típusok közvetlenül tárolják az adataikat. Egy struktúra default értéke minden mezőhöz 0-hoz van hozzárendelve. 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 default értékhez rendelt struktúra inicializálva van a 0-bites mintázatra. A new értékkel inicializált struktúra nullás bitmintára van inicializálva, amit a mező inicializálók és egy konstruktor végrehajtása követ.

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 ()
}

Minden struct rendelkezik egy public paraméter nélküli konstruktorral. 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 tömör szintaxist biztosítanak a konstruktorparaméterekhez, amelyek az struct törzsben, az adott szerkezet bármely tagdeklarációjában használhatók.

Ha egy struktúratípus összes példánymezője elérhető, akkor az new operátor nélkül is példányosíthatja. 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ípusokeseté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:

  • 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 érték szerinti átadás befolyásolhatja a kód teljesítményét nagy teljesítményű esetekben, amelyek nagy struktúratí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, out, in, vagy ref readonly metódusparaméter-módosítókat használja annak jelzésére, hogy egy argumentumot hivatkozással kell átadni. Használja a ref returneket, hogy egy metódus eredményét hivatkozással adja vissza. További információért lásd: A foglalások elkerülése.

struct korlátozás

A kényszerbenstructstruct 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ípus esetében (kivéve ref struct típusokat) léteznek boxing és unboxing konverziók, amelyek System.ValueType és System.Object típusok között történhetnek. Léteznek boxing és unboxing konverziók egy struktúratípus és bármely interfész között, amelyet a struktúratípus 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:

Lásd még