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 jakoreadonly
, bez ohledu na přítomnostreadonly
modifikátoru v deklaraci vlastnosti.Modifikátor můžete použít
readonly
u vlastnosti nebo indexeru s příslušenstvíminit
: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 struct
typ . 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ý struct
má public
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
, in
nebo 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í:
- Readonly – struktury
- Členové instance jen pro čtení
- C# 10 – konstruktory struktury bez parametrů
- C# 10 – Povolení
with
výrazu u struktur - C# 10 – Záznam struktur
- C# 11 – automatické výchozí struktury