Sdílet prostřednictvím


Typy struktur (referenční dokumentace jazyka C#)

Typ struktury (nebo typ struktury) je typ hodnoty, který může zapouzdřovat data a související funkce. Pomocí klíčového struct slova definujete typ struktury:

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

Informace o ref struct typech a readonly ref struct typech najdete v článku o typech struktury ref.

Typy struktur mají sémantiku hodnot. To znamená, že proměnná typu struktury obsahuje instanci typu. Ve výchozím nastavení se hodnoty proměnných zkopírují při přiřazení, předají argument metodě a vrátí výsledek metody. U proměnných typu struktury se zkopíruje instance typu. Další informace naleznete v tématu Typy hodnot.

Typy struktur se obvykle používají k návrhu malých typů orientovaných na data, které poskytují malé nebo žádné chování. Například .NET používá typy struktur k reprezentaci čísla (celé číslo i reálné), logické hodnoty, znaku Unicode, časové instance. Pokud se zaměřujete na chování typu, zvažte definování třídy. Typy tříd mají referenční sémantiku. To znamená, že proměnná typu třídy obsahuje odkaz na instanci typu, nikoli samotnou instanci.

Vzhledem k tomu, že typy struktur mají sémantiku hodnot, doporučujeme definovat neměnné typy struktur.

readonly Struct

Modifikátor použijete readonly k deklaraci, že typ struktury je neměnný. Všechny datové členy readonly struktury musí být jen pro čtení:

  • Každá deklarace pole musí mít readonly modifikátor.
  • Všechny vlastnosti, včetně automaticky implementovaných, musí být jen pro čtení nebo init jen pro čtení. Všimněte si, že inicializační settery jsou k dispozici pouze v jazyce C# verze 9.

To zaručuje, že žádný člen readonly struktury modifikuje stav struktury. To znamená, že ostatní členy instance s výjimkou konstruktorů jsou implicitně readonly.

Poznámka:

Ve struktuře readonly může datový člen proměnlivého referenčního typu stále ztlumit svůj vlastní stav. Instanci například nemůžete nahradit List<T> , ale do ní můžete přidat nové prvky.

Následující kód definuje readonly strukturu s init-only vlastnost setters:

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 členové instance

Modifikátor můžete také použít readonly k deklaraci, že člen instance neupravuje stav struktury. Pokud nemůžete deklarovat celý typ struktury jako readonly, použijte readonly modifikátor k označení členů instance, které nemění stav struktury.

V rámci člena readonly instance nemůžete přiřadit pole instance struktury. readonly Člen ale může volat jinéhoreadonly člena. V takovém případě kompilátor vytvoří kopii instance struktury a zavolá nečlenareadonly na této kopii. V důsledku toho se původní instance struktury nezmění.

Modifikátor obvykle použijete readonly u následujících typů členů instance:

  • metody:

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

    Modifikátor můžete použít readonly také u metod, které přepisují metody deklarované v System.Object:

    public readonly override string ToString() => $"({X}, {Y})";
    
  • vlastnosti a indexery:

    private int counter;
    public int Counter
    {
        readonly get => counter;
        set => counter = value;
    }
    

    Pokud potřebujete modifikátor použít readonly u obou přístupových objektů vlastnosti nebo indexeru, použijte ho v deklaraci vlastnosti nebo indexeru.

    Poznámka:

    Kompilátor deklaruje get příslušenství automaticky implementované vlastnosti jako readonly, bez ohledu na přítomnost readonly modifikátoru v deklaraci vlastnosti.

    Modifikátor můžete použít readonly u vlastnosti nebo indexeru s příslušenstvím init :

    public readonly double X { get; init; }
    

Modifikátor můžete použít readonly u statických polí typu struktury, ale ne u jiných statických členů, jako jsou vlastnosti nebo metody.

Kompilátor může použít readonly modifikátor pro optimalizaci výkonu. Další informace najdete v tématu Zabránění přidělení.

Nedestruktivní mutaci

Počínaje jazykem C# 10 můžete pomocí výrazu with vytvořit kopii instance typu struktury se zadanými vlastnostmi a poli upravenými. Syntaxe inicializátoru objektů slouží k určení členů, které se mají upravit, a jejich nových hodnot, jak ukazuje následující příklad:

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

Počínaje jazykem C# 10 můžete definovat typy struktury záznamů. Typy záznamů poskytují integrované funkce pro zapouzdření dat. Můžete definovat oba record struct typy i readonly record struct typy. Struktura záznamu nemůže být ref struct. Další informace a příklady najdete v tématu Záznamy.

Vložená pole

Počínaje jazykem C# 12 můžete deklarovat vložená pole jako struct typ:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
    private char _firstElement;
}

Vložené pole je struktura, která obsahuje souvislý blok N prvků stejného typu. Jedná se o ekvivalent bezpečného kódu deklarace pevné vyrovnávací paměti , která je k dispozici pouze v nebezpečném kódu. Vložené pole je s struct následujícími vlastnostmi:

  • Obsahuje jedno pole.
  • Struktura nezadá explicitní rozložení.

Kromě toho kompilátor ověří System.Runtime.CompilerServices.InlineArrayAttribute atribut:

  • Délka musí být větší než nula (> 0).
  • Cílový typ musí být struktura.

Ve většině případů lze k vložené matici přistupovat jako k poli, a to jak ke čtení, tak k zápisu hodnot. Kromě toho můžete použít operátory rozsahu a indexu.

Existují minimální omezení pro typ jednoho pole vloženého pole. Nemůže to být typ ukazatele:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
    private unsafe char* _pointerElement;    // CS9184
}

ale může to být libovolný typ odkazu nebo jakýkoli typ hodnoty:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
    private string _referenceElement;
}

Vložená pole můžete použít s téměř jakoukoli datovou strukturou jazyka C#.

Vložená pole jsou pokročilou jazykovou funkcí. Jsou určené pro vysoce výkonné scénáře, kdy je vložený souvislý blok prvků rychlejší než jiné alternativní datové struktury. Další informace o vložených polích najdete ve specifikaci funkce.

Inicializace struktury a výchozí hodnoty

Proměnná struct typu přímo obsahuje data pro tento structtyp . Tím se vytvoří rozdíl mezi neinicializovaným struct, který má výchozí hodnotu a inicializovaný struct, který ukládá hodnoty nastavené jeho sestavením. Představte si například následující kód:

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

Jak ukazuje předchozí příklad, výchozí výraz hodnoty ignoruje konstruktor bez parametrů a vytvoří výchozí hodnotu typu struktury. Instance pole typu struktury také ignoruje konstruktor bez parametrů a vytvoří pole naplněné výchozími hodnotami typu struktury.

Nejběžnější situace, kdy vidíte výchozí hodnoty, jsou v polích nebo v jiných kolekcích, kde interní úložiště obsahuje bloky proměnných. Následující příklad vytvoří pole 30 TemperatureRange struktur, z nichž každá má výchozí hodnotu:

// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];

Všechna pole členů struktury musí být při vytváření určitě přiřazena , protože struct typy přímo ukládají data. Hodnota default struktury má rozhodně přiřazena všechna pole 0. Všechna pole musí být při vyvolání konstruktoru rozhodně přiřazena. Pole inicializujete pomocí následujících mechanismů:

  • Inicializátory polí můžete přidat do libovolného pole nebo automaticky implementované vlastnosti.
  • V těle konstruktoru můžete inicializovat všechna pole nebo automatické vlastnosti.

Počínaje jazykem C# 11, pokud neinicializujete všechna pole ve struktuře, kompilátor přidá kód do konstruktoru, který inicializuje tato pole na výchozí hodnotu. Kompilátor provádí svoji obvyklou analýzu přiřazení. Všechna pole, ke kterým se přistupuje před přiřazením nebo která nejsou rozhodně přiřazena při dokončení provádění konstruktoru, mají přiřazené výchozí hodnoty před spuštěním těla konstruktoru. Je-li this přístup před přiřazením všech polí, struktura se inicializuje na výchozí hodnotu před provedením těla konstruktoru.

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

Každý structpublic konstruktor bez parametrů. Pokud napíšete konstruktor bez parametrů, musí být veřejný. Pokud struktura deklaruje všechny inicializátory polí, musí explicitně deklarovat konstruktor. Tento konstruktor nemusí být bez parametrů. Pokud struktura deklaruje inicializátor pole, ale žádné konstruktory, kompilátor hlásí chybu. Jakýkoli explicitně deklarovaný konstruktor (s parametry nebo bez parametrů) spustí všechny inicializátory polí pro danou strukturu. Všechna pole bez inicializátoru pole nebo přiřazení v konstruktoru jsou nastavena na výchozí hodnotu. Další informace naleznete v návrhu návrhu konstruktorů bez parametrů.

Počínaje jazykem C# 12 struct mohou typy definovat primární konstruktor jako součást deklarace. Primární konstruktory poskytují stručnou syntaxi pro parametry konstruktoru struct , které lze použít v celém těle, v jakékoli deklaraci členu pro tuto strukturu.

Pokud jsou všechna pole instance typu struktury přístupná, můžete ji vytvořit i bez operátoru new . V takovém případě musíte inicializovat všechna pole instance před prvním použitím instance. Následující příklad ukazuje, jak to udělat:

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

V případě předdefinovaných typů hodnot zadejte hodnotu typu pomocí odpovídajících literálů.

Omezení návrhu typu struktury

Struktury mají většinu schopností typu třídy . Existují některé výjimky a některé výjimky, které byly odebrány v novějších verzích:

  • Typ struktury nemůže dědit z jiné třídy nebo typu struktury a nemůže být základem třídy. Typ struktury však může implementovat rozhraní.
  • Finalizátor nelze deklarovat v rámci typu struktury.
  • Před jazykem C# 11 musí konstruktor typu struktury inicializovat všechna pole instance tohoto typu.

Předávání proměnných typu struktury odkazem

Když metodě předáte proměnnou typu struktury jako argument nebo vrátíte hodnotu typu struktury z metody, zkopíruje se celá instance typu struktury. Předání hodnoty může ovlivnit výkon kódu ve scénářích s vysokým výkonem, které zahrnují velké typy struktur. Kopírováním hodnot se můžete vyhnout předáním proměnné typu struktury odkazem. Pomocí modifikátorů parametru ref, , out, innebo ref readonly metody indikujte, že argument musí být předán odkazem. K vrácení výsledku metody odkazem použijte odkaz . Další informace najdete v tématu Vyhněte se přidělování.

Omezení struktury

Pomocí klíčového struct slova v struct omezení můžete také určit, že parametr typu je nenulový typ hodnoty. Typy struktur i výčtů struct splňují omezení.

Převody

Pro jakýkoli typ struktury (s výjimkou ref struct typů) existují převody boxingu a rozbalení do a z typůSystem.ValueType.System.Object Existují také boxing a rozbalení převodů mezi typem struktury a jakýmkoli rozhraním, které implementuje.

specifikace jazyka C#

Další informace najdete v části Struktury specifikace jazyka C#.

Další informace o struct funkcích najdete v následujících poznámkách k návrhu funkcí:

Viz také