Struktury jazyka C#

Návod

Začínáte s vývojem softwaru? Začněte nejprve kurzy Začínáme . Jakmile v kódu potřebujete odlehčené typy hodnot, setkáte se s strukturami.

Máte zkušenosti v jiném jazyce? Struktury v jazyce C# jsou hodnotové typy podobné strukturám v jazyce C++ nebo Swift, ale při zabalení jsou uloženy ve spravované paměti a podporují rozhraní, konstruktory a metody. Prohlédněte si sekci struktur jen pro čtení, které jsou specifické pro jazyk C#. Struktury záznamů najdete v tématu Záznamy.

Struktura je hodnotový typ, který uchovává svá data přímo v instanci, nikoli přes odkaz na objekt v haldě. Když přiřadíte strukturu nové proměnné, modul runtime zkopíruje celou instanci. Změny jedné proměnné nemají vliv na druhou, protože každá proměnná představuje jinou instanci. Používejte struktury pro malé, odlehčené typy, jejichž primární role ukládá data místo modelování chování. Mezi příklady patří souřadnice, barvy, měření nebo nastavení konfigurace.

Kdy použít struktury

Pokud typ používáte, použijte strukturu:

  • Představuje jednu hodnotu nebo malou skupinu souvisejících hodnot (přibližně 16 bajtů nebo méně).
  • Má sémantiku hodnot – dvě instance se stejnými daty by měly být stejné.
  • Je primárně datový kontejner, nikoli model chování.
  • Nepotřebuje dědičnost ze základního typu (struktury nemohou dědit z jiných struktur nebo tříd, ale mohou implementovat rozhraní).

Pro širší porovnání, které zahrnuje třídy, záznamy, n-tice a rozhraní, si přečtěte Vyberte druh typu.

Deklarace struktury

Definujte strukturu pomocí klíčového struct slova. Struktura může obsahovat pole, vlastnosti, metody a konstruktory stejně jako třída:

struct Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public readonly double DistanceTo(Point other)
    {
        var dx = X - other.X;
        var dy = Y - other.Y;
        return Math.Sqrt(dx * dx + dy * dy);
    }

    public override string ToString() => $"({X}, {Y})";
}

Struktura Point ukládá dvě double hodnoty a poskytuje metodu pro výpočet vzdálenosti mezi dvěma body. Metoda DistanceTo je označena readonly , protože neupravuje stav struktury. Tento vzor je pokrytý členy jen pro čtení.

Sémantika hodnot

Struktury jsou typy hodnot. Přiřazení kopíruje data, takže každá proměnná obsahuje vlastní nezávislou kopii:

var p1 = new Point { X = 3, Y = 4 };
var p2 = p1; // copies the data
p2.X = 10;

Console.WriteLine(p1); // (3, 4)  — p1 is unchanged
Console.WriteLine(p2); // (10, 4) — only p2 was modified

Vzhledem k tomu, že struktury jsou datové kontejnery, přiřazení zkopíruje každý datový člen do nové nezávislé instance. Každá kopie je odlišná. Úprava jednoho nemá vliv na druhý. Toto chování se liší od tříd, kdy přiřazení kopíruje pouze odkaz a obě proměnné sdílejí stejný objekt. Další informace o rozlišení naleznete v tématu Typy hodnot a odkazové typy.

Konstruktory struktury

Konstruktory můžete definovat ve strukturách stejným způsobem jako v třídách. Struktury mohou mít konstruktory bez parametrů , které nastavují vlastní výchozí hodnoty. Pojem "konstruktor bez parametrů" rozlišuje instanci vytvořenou new, která spouští logiku konstruktoru, od výchozí instance vytvořené pomocí výrazu default (který inicializuje všechna pole na nulové hodnoty):

struct ConnectionSettings
{
    public string Host { get; set; }
    public int Port { get; set; }
    public int MaxRetries { get; set; }

    public ConnectionSettings()
    {
        Host = "localhost";
        Port = 8080;
        MaxRetries = 3;
    }
}

Konstruktor bez parametrů se spustí při použití new bez argumentů. Výraz default obchází konstruktor a nastaví všechna pole na výchozí hodnoty (0, null, false). Mějte na paměti rozdíl:

var custom = new ConnectionSettings();
Console.WriteLine($"{custom.Host}:{custom.Port} (retries: {custom.MaxRetries})");
// localhost:8080 (retries: 3)

var defaults = default(ConnectionSettings);
Console.WriteLine($"{defaults.Host ?? "(null)"}:{defaults.Port} (retries: {defaults.MaxRetries})");
// (null):0 (retries: 0)

Kompilátor automaticky inicializuje všechna pole, která explicitně nenastavíte v konstruktoru. Inicializovat můžete pouze pole, která potřebují jiné než výchozí hodnoty:

struct GameTile
{
    public int Row { get; set; }
    public int Column { get; set; }
    public bool IsBlocked { get; set; }

    public GameTile(int row, int column)
    {
        Row = row;
        Column = column;
        // IsBlocked is automatically initialized to false
    }
}

Následující příklad zobrazí výchozí hodnotu pro IsBlocked:

var tile = new GameTile(2, 5);
Console.WriteLine($"Tile ({tile.Row}, {tile.Column}), blocked: {tile.IsBlocked}");
// Tile (2, 5), blocked: False

Vlastnost IsBlocked není v konstruktoru přiřazena, takže kompilátor ji nastaví na false (výchozí hodnota pro bool). Tato funkce omezuje nadbytečný kód v konstruktorech, které potřebují nastavit pouze několik polí.

Struktury jen pro čtení a členy jen pro čtení

Záruka readonly struct zajišťuje, že žádný instanční člen nemodifikuje stav struktury. Kompilátor tuto záruku vynucuje tím, že vyžaduje, aby všechna pole a automaticky implementované vlastnosti byly jen pro čtení:

readonly struct Temperature
{
    public double Celsius { get; }

    public Temperature(double celsius) => Celsius = celsius;

    public double Fahrenheit => Celsius * 9.0 / 5.0 + 32.0;

    public override string ToString() => $"{Celsius:F1}°C ({Fahrenheit:F1}°F)";
}

Následující příklad vytvoří Temperature instanci a přečte její vlastnosti:

var temp = new Temperature(100);
Console.WriteLine(temp); // 100.0°C (212.0°F)
// temp.Celsius = 50; // Error: property is read-only

Pokud nepotřebujete, aby celá struktura byla neměnná, označte jednotlivé členy jako readonly místo toho. Člen readonly nemůže změnit stav struktury a kompilátor ověří, že zaručuje:

struct Velocity
{
    public double X
    {
        readonly get;
        set;
    }

    public double Y
    {
        readonly get;
        set;
    }

    public readonly double Speed => Math.Sqrt(X * X + Y * Y);

    public readonly override string ToString() => $"({X}, {Y}) speed={Speed:F2}";
}

Následující příklad ukazuje, že readonly členové vracejí aktualizované hodnoty při změně proměnlivých vlastností:

var v = new Velocity { X = 3, Y = 4 };
Console.WriteLine(v.Speed); // 5
Console.WriteLine(v);       // (3, 4) speed=5.00
v.X = 6;
Console.WriteLine(v.Speed); // 7.211...

Označení členů readonly pomáhá kompilátoru optimalizovat obranné kopie. Když předáte readonly strukturu metodě, která přijímá in parametr, kompilátor ví, že není potřeba kopírovat.

Viz také