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 implementáltakat is, írásvédettnek vagy init csak olvashatónak kell lennie.

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 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, 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 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ágreadonlytartozékát deklaráljaget, függetlenül attól, hogy a módosító szerepel-e a readonly 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.

Az egyetlen mező típusára minimális korlátozások vonatkoznak. Nem lehet mutatótípus, de lehet bármilyen hivatkozástípus vagy értéktípus. 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 structtartozó 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. refA , 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ő structstruct 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:

Lásd még