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

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 kopírují při přiřazení, předávají argument metodě a vrací výsledek metody. U proměnných typu struktury se zkopíruje instance typu. Další informace najdete v tématu Typy hodnot.

Typy struktur obvykle slouží 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

Počínaje jazykem C# 7.2 pomocí modifikátoru readonly deklarujete, že typ struktury je neměnný. Všechny datové členy readonly struktury musí být jen pro čtení:

To zaručuje, že žádný člen readonly struktury upraví stav struktury. V jazyce C# 8.0 a novějším to znamená, že ostatní členy instance kromě konstruktorů jsou implicitně readonly.

Poznámka

readonly Ve struktuře může datový člen ztlumení ztlumit svůj vlastní stav. Instanci například nemůžete nahradit List<T> , ale můžete do ní přidat nové prvky.

Následující kód definuje readonly strukturu s init-only vlastnost setters, k dispozici v C# 9.0 a novější:

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

Počínaje jazykem readonly C# 8.0 můžete také pomocí modifikátoru deklarovat, že člen instance nezmění stav struktury. Pokud nemůžete deklarovat celý typ struktury jako readonly, pomocí readonly modifikátoru označte členy instance, které nemění stav struktury.

readonly V rámci člena instance nemůžete přiřadit pole instance struktury. readonly Člen ale může volat jiného členareadonly. 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 použít readonly modifikátor pro oba přístupové objekty 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.

    V jazyce C# 9.0 a novějším můžete modifikátor 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 optimalizace výkonu. Další informace najdete v tématu Zápis bezpečného a efektivního kódu jazyka C#.

Nedestruktivní mutací

Počínaje jazykem C# 10 můžete pomocí výrazuwith 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é hodnoty, 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 strukturouref. Další informace a příklady najdete v tématu Záznamy.

Inicializace struktury a výchozí hodnoty

Proměnná struct typu obsahuje přímo 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é vytvoření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ří matici naplněnou výchozími hodnotami typu struktury.

Nejběžnější situace, kdy uvidíte, že výchozí hodnoty jsou v polích nebo v jiných kolekcích, kde interní úložiště zahrnuje bloky proměnných. Následující příklad vytvoří matici 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í svá data. Hodnota default struktury jednoznačně přiřadila všechna pole 0. Všechna pole musí být při vyvolání konstruktoru rozhodně přiřazená. Inicializujete pole pomocí následujících mechanismů:

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

Počínaje C# 11, pokud neicializujete 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 nejsou rozhodně přiřazena, když konstruktor dokončí provádění, mají přiřazené výchozí hodnoty před provedením textu konstruktoru. Pokud this se přistupuje před přiřazením všech polí, struktura se inicializuje na výchozí hodnotu před provedením textu 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 nezapíšete veřejný konstruktor bez parametrů, kompilátor ho vygeneruje. Konstruktor vygenerovaný kompilátorem bez parametrů spustí všechny inicializátory polí a vytvoří výchozí hodnotu pro všechna ostatní pole. Pokud deklarujete jakékoli inicializátory polí, musíte deklarovat jeden explicitní konstruktor. Jedním explicitním konstruktorem může být konstruktor bez parametrů. Může mít prázdné tělo. Další informace najdete v poznámce k návrhu návrhu funkcí konstruktorů bez parametrů .

Pokud jsou všechna pole instance typu struktury přístupná, můžete ji vytvořit také 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 použijte odpovídající literály k určení hodnoty typu.

Omezení návrhu typu struktury

Struktury mají většinu možností 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 nemůžete deklarovat v rámci typu struktury.
  • Před C# 11 musí konstruktor typu struktury inicializovat všechna pole instance typu.
  • Před C# 10 nemůžete deklarovat konstruktor bez parametrů.
  • Před C# 10 nemůžete inicializovat pole instance nebo vlastnost v jeho deklaraci.

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

Když předáte proměnnou typu struktury metodě jako argument nebo vrátíte hodnotu typu struktury z metody, zkopíruje se celá instance typu struktury. Předání hodnotou 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, nebo outmetody označte, in že argument musí být předán odkazem. K vrácení výsledku metody pomocí odkazu použijte odkaz . Další informace najdete v tématu Psaní bezpečného a efektivního kódu jazyka C#.

ref Struct

Počínaje jazykem C# 7.2 můžete použít ref modifikátor v deklaraci typu struktury. ref Instance typu struktury se přidělují do zásobníku a nemůžou utéct do spravované haldy. Aby se zajistilo, kompilátor omezuje použití ref typů struktur následujícím způsobem:

  • Strukturou ref nemůže být typ prvku pole.
  • Struktura ref nemůže být deklarovaný typ pole třídy nebo nestrukturovanéref .
  • Struktura ref nemůže implementovat rozhraní.
  • Strukturu ref nelze rámeček na System.ValueType nebo System.Object.
  • Strukturou ref nemůže být argument typu.
  • Proměnnou ref struktury nelze zachytit výrazem lambda ani místní funkcí.
  • Proměnnou ref struktury nelze použít v async metodě. Proměnné struktury však můžete použít ref v synchronních metodách, například v metodách, které vracejí Task nebo Task<TResult>.
  • Proměnnou ref struktury nelze použít v iterátorech.

Počínaje jazykem C# 8.0 můžete definovat jednorázovou ref strukturu. Za tímto účelem se ujistěte, že ref struktura odpovídá jednorázovému vzoru. To znamená, že má instanci nebo metodu rozšíření Dispose , která je přístupná, bez parametrů a má návratový void typ.

Obvykle definujete ref typ struktury, pokud potřebujete typ, který zahrnuje také datové členy ref typů struktur:

public ref struct CustomRef
{
    public bool IsValid;
    public Span<int> Inputs;
    public Span<int> Outputs;
}

Chcete-li deklarovat ref strukturu jako readonly, kombinujte readonly a ref modifikátory v deklaraci typu ( readonly modifikátor musí být před ref modifikátorem):

public readonly ref struct ConversionRequest
{
    public ConversionRequest(double rate, ReadOnlySpan<double> values)
    {
        Rate = rate;
        Values = values;
    }

    public double Rate { get; }
    public ReadOnlySpan<double> Values { get; }
}

V .NET jsou příklady ref struktury System.Span<T> a System.ReadOnlySpan<T>.

Omezení struktury

Pomocí klíčového struct slova v struct omezení také určíte, že parametr typu je typ hodnoty, který nemá hodnotu null. Typy struktur i výčtůstruct splňují omezení.

Převody

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

specifikace jazyka C#

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

Další informace o funkcích zavedených v C# 7.2 a novějších najdete v následujících poznámkách k návrhu funkcí:

Viz také